KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > groboutils > junit > v1 > iftc > InterfaceTestCase


1 /*
2  * @(#)InterfaceTestCase.java
3  *
4  * Copyright (C) 2002-2003 Matt Albrecht
5  * groboclown@users.sourceforge.net
6  * http://groboutils.sourceforge.net
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */

26
27 package net.sourceforge.groboutils.junit.v1.iftc;
28
29
30 import junit.framework.TestCase;
31
32 import java.io.StringWriter JavaDoc;
33 import java.io.PrintWriter JavaDoc;
34
35 import java.util.Stack JavaDoc;
36
37
38 /**
39  * A subclass of TestCase to ease the requirements of creating an
40  * interface test. The tests should be thought of as "contract tests".
41  * Subclasses can call <tt>createImplObject()</tt> to create a new instance
42  * of a subclass of <tt>interfaceClass</tt>, which is generated from the
43  * <tt>ImplFactory</tt> passed into the constructor.
44  * <P>
45  * Subclasses that want to use the InterfaceTestSuite helper class will need
46  * to specify a constructor similar to:
47  * <PRE>
48  * public MyClassTest( String name, ImplFactory f )
49  * {
50  * super( name, MyClass.class, f );
51  * }
52  * </PRE>
53  * where <tt>MyClass</tt> is the interface or base class under test.
54  * <P>
55  * As of October 30, 2002, the InterfaceTestCase has a slightly different
56  * behavior when the factory instance is an implementation of ICxFactory.
57  * In this scenario, it will store all the instantiated objects in the stack,
58  * and each instantiated object will be passed to the ICxFactory instance
59  * during the test's normal tearDown. If the ICxFactory throws an exception
60  * during any of the tearDown calls, they will be stored up and reported
61  * in a single exception. Therefore, if you want this functionality, then
62  * you will need to ensure that your <tt>tearDown()</tt> method calls the
63  * super <tt>tearDown()</tt>.
64  * <P>
65  * Even though JUnit 3.8+ allows for a TestCase to have a default (no-arg)
66  * constructor, the <tt>InterfaceTestCase</tt> does not support this. The
67  * benefits simply aren't there for interface tests: they will still have to
68  * create a constructor which passes <tt>InterfaceTestCase</tt> which class
69  * is being tested. Since a constructor is required anyway, the little extra
70  * effort to add two arguments to the constructor and call to the super
71  * is trivial compared to not needing the constructor at all.
72  * <P>
73  * As of 08-Dec-2002, the returned name of the test can include the class's
74  * name, without the package, to improve traceability. This will allow
75  * the user to be able to see in which specific test class an error occured
76  * through the Ant JUnit report mechanism. This is enabled by default, but
77  * can be disabled by setting the Java system-wide property
78  * "net.sourceforge.groboutils.junit.v1.iftc.InterfaceTestCase.no-classname"
79  * to <tt>true</tt>,
80  * which is dynamically checked at runtime at each call.
81  *
82  * @author Matt Albrecht <a HREF="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
83  * @version $Date: 2003/02/10 22:52:20 $
84  * @since March 2, 2002
85  * @see ImplFactory
86  * @see ICxFactory
87  * @see InterfaceTestSuite
88  */

89 public abstract class InterfaceTestCase extends TestCase
90 {
91     // package protected for test-case use.
92
static final String JavaDoc DONT_USE_CLASSNAME =
93         InterfaceTestCase.class.getName() + ".no-classname";
94     
95     private ImplFactory factory;
96     private Class JavaDoc interfaceClass;
97     
98     // Due to possible memory-leak, this stack will only contain
99
// instantiated objects when the factory is of type ICxFactory.
100
private Stack JavaDoc instantiatedObjects = new Stack JavaDoc();
101     
102     // As a slight optimization, we will cache the check if the
103
// factory is an instance of ICxFactory or not.
104
private boolean isICxFactory = false;
105     
106     // allows for manual setting of the classname display in the output name
107
private Boolean JavaDoc useClassInName = null;
108     
109     
110     /**
111      * The standard constructor used by JUnit up to version 3.7.
112      *
113      * @param name the name of the test to execute.
114      * @param interfaceClass the class which this test case tests.
115      * @param f the factory which will create specific subclass instances.
116      */

117     public InterfaceTestCase( String JavaDoc name, Class JavaDoc interfaceClass, ImplFactory f )
118     {
119         super( name );
120         if (interfaceClass == null || f == null)
121         {
122             throw new IllegalArgumentException JavaDoc("no null arguments");
123         }
124         
125         // need to ensure that a common test coding error didn't occur...
126
assertTrue(
127             "Interface under test argument ("+interfaceClass.getName()+
128             ") is the same as the current test's class ("+
129             getClass().getName()+
130             "). The correct usage is to pass in the Class object which "+
131             "corresponds to the superclass or interface all instance methods "+
132             "tested must extend.",
133             !getClass().equals( interfaceClass ) );
134         
135         // the class can be an interface, an abstract class, or just
136
// a regular class. We don't care as long as it isn't null.
137
this.interfaceClass = interfaceClass;
138         this.factory = f;
139         
140         // cache the assertion for if the factory is an ICxFactory instance.
141
if (f instanceof ICxFactory)
142         {
143             this.isICxFactory = true;
144         }
145     }
146     
147     
148     /**
149      * Sets whether the classname is put in the output or not. If you don't
150      * set this value here, it will use the value of the
151      * system property described above.
152      *
153      * @since 03-Dec-2002
154      */

155     public void setUseClassInName( boolean use )
156     {
157         this.useClassInName = new Boolean JavaDoc( use );
158     }
159     
160     
161     
162     
163     /**
164      * Calls the stored factory to create an implemented object. Subclasses
165      * should make their own method, say <tt>getObject()</tt>, which returns
166      * this method's result, but casted to the right class.
167      * <P>
168      * This method makes an assertion that the factory's created object is not
169      * <tt>null</tt>, so that the system state is ensured. Therefore,
170      * this method will never return <tt>null</tt>. Also, this method asserts
171      * that the created object is of the correct type (as passed in through
172      * the constructor), so that it can be correctly cast without errors.
173      *
174      * @return the object created by the factory.
175      */

176     public Object JavaDoc createImplObject()
177     {
178         // ensure the factory was set.
179
assertNotNull(
180             "The factory instance was never set.",
181             this.factory );
182         
183         Object JavaDoc o;
184         try
185         {
186             o = this.factory.createImplObject();
187         }
188         catch (Exception JavaDoc ex)
189         {
190             // allow for the factory creation to throw exceptions.
191
fail( "Factory "+this.factory.toString()+
192                 " threw exception "+ex+" during creation: "+
193                 exceptionToString( ex ) );
194             // the above call will always exit, but the compiler doesn't
195
// know that. So to make it happy the next line has been added.
196
o = null;
197         }
198         assertNotNull(
199             "The implementation factory "+this.factory+" created a null.",
200             o );
201         
202         // Since the generated object is non-null, we will store it in our
203
// stack, even if the next assert fails. This allows for correct
204
// deconstruction of *every* non-null generated object.
205
if (this.isICxFactory)
206         {
207             this.instantiatedObjects.push( o );
208         }
209         
210         assertTrue(
211             "The implementation factory did not create a valid class: created "+
212             o.getClass().getName()+", but should have been of type "+
213             getInterfaceClass().getName()+".",
214             getInterfaceClass().isInstance( o ) );
215         return o;
216     }
217     
218     
219     /**
220      * Return the interface or abstract class this test covers.
221      *
222      * @return the interface under test.
223      */

224     public Class JavaDoc getInterfaceClass()
225     {
226         return this.interfaceClass;
227     }
228     
229     
230     /**
231      * Override the TestCase default getName so that the factory names are
232      * returned as well.
233      *
234      * @return the method name being tested, along with the factory's
235      * name.
236      */

237     public String JavaDoc getName()
238     {
239         return getNamePrefix() + super.getName() +
240             "[" + this.factory.toString() +"]";
241     }
242     
243     
244     /**
245      * Ensure, for JUnit 3.7 support, that the original name() method is
246      * still supported.
247      *
248      * @return getName().
249      */

250     public String JavaDoc name()
251     {
252         return getName();
253     }
254     
255     
256     /**
257      * Send each instantiated object to the factory for cleanup.
258      *
259      * @exception Exception thrown if the super's tearDown throws an
260      * exception, or if any exceptions are thrown during the tear-down
261      * of the factory generated instances.
262      */

263     protected void tearDown() throws Exception JavaDoc
264     {
265         if (this.isICxFactory)
266         {
267             int errorCount = 0;
268             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(
269                 "Encountered factory tearDown exceptions: " );
270             ICxFactory cf = (ICxFactory)this.factory;
271             while (!this.instantiatedObjects.isEmpty())
272             {
273                 try
274                 {
275                     cf.tearDown( this.instantiatedObjects.pop() );
276                 }
277                 catch (ThreadDeath JavaDoc td)
278                 {
279                     // never swallow thread death exceptions
280
throw td;
281                 }
282                 catch (Throwable JavaDoc t)
283                 {
284                     // catch all factory exceptions, and sum them up into
285
// a single exception at the end.
286
if (errorCount > 0)
287                     {
288                         sb.append( "; " );
289                     }
290                     sb.append( t.toString() );
291                     
292                     ++errorCount;
293                     
294                     // Tell the user about this exception.
295
t.printStackTrace();
296                 }
297             }
298             // only do the assertion *after* all the generated objects have
299
// been torn down.
300
assertTrue( sb.toString(), errorCount <= 0 );
301         }
302         
303         // tell the super-class to tear itself down.
304
super.tearDown();
305     }
306     
307     
308     /**
309      * Allow for easy translation of exceptions to strings, including
310      * stack traces.
311      *
312      * @param t the exception to translate into a string.
313      * @return the exception + stack trace as a string.
314      */

315     private String JavaDoc exceptionToString( Throwable JavaDoc t )
316     {
317         if (t == null)
318         {
319             return "<null exception>";
320         }
321         StringWriter JavaDoc sw = new StringWriter JavaDoc();
322         PrintWriter JavaDoc pw = new PrintWriter JavaDoc( sw );
323         t.printStackTrace( pw );
324         pw.flush();
325         return sw.toString();
326     }
327     
328     
329     /**
330      * Generate the prefix for the name of this test class.
331      *
332      * @return the prefix string
333      * @since 08-Dec-2002
334      */

335     private String JavaDoc getNamePrefix()
336     {
337         boolean usePrefix;
338         if (this.useClassInName != null)
339         {
340             usePrefix = this.useClassInName.booleanValue();
341         }
342         else
343         {
344             usePrefix = !Boolean.getBoolean( DONT_USE_CLASSNAME );
345         }
346         String JavaDoc ret = "";
347         if (usePrefix)
348         {
349             // get the classname of the current test, without the package.
350
ret = this.getClass().getName();
351             int pos = ret.lastIndexOf( '.' );
352             if (pos >= 0)
353             {
354                 ret = ret.substring( pos+1 );
355             }
356             ret = ret + '.';
357         }
358         return ret;
359     }
360 }
361
362
Popular Tags