Reputation: 4753
Using JUnit4, what I want to do is to be able to test a group of different java projects that all do the same thing, but instead of have to write out a test case to test each project i was wondering is it possible to write a single test that can be run on multiple classes?
If this is not possible using JUnit4, is it possible to do this any other way?
I know this isnt right but this is just to give a brief idea of what I am on about:
@Test
public void test(Class insertClassNameHere, Method nameOfMethod){
Class insertClassNameHere = new Class();
assertEquals(insertClassNameHere.nameOfMethod(),1);
}
Upvotes: 3
Views: 9344
Reputation: 879
Sorry for jumping into this so late. Having a similar need, I have developed a number of presentations and trainings that demonstrate the change over time to a given class after refactoring. Thus, the tests are the same, but the class itself changes. Each version is contained in a different package. The class name remains the same. This sounds similar to what the OP was facing.
I find the parameterized solution cumbersome. I have used two solutions. They are both written in Groovy, but the second example below should be implementable as a pure java solution.
Here is the first:
public class ItemTest {
def defaultConstructedClasses = [
new com.groovifyingjava.essentialgroovification.Item()
,new com.groovifyingjava.rewriteequals.Item()
,new com.groovifyingjava.removesemicolons.Item()
,new com.groovifyingjava.removeparentheses.Item()
,new com.groovifyingjava.removeaccessors.Item()
,new com.groovifyingjava.removeconstructors.Item()
,new com.groovifyingjava.removeimports.Item()
,new com.groovifyingjava.removereturn.Item()
,new com.groovifyingjava.clarifyidentityequality.Item()
,new com.groovifyingjava.coercetypes.Item()
,new com.groovifyingjava.defaultaccessmodifiers.Item()
,new com.groovifyingjava.eachiteration.Item()
,new com.groovifyingjava.fieldaccessnotation.Item()
,new com.groovifyingjava.namedparameters.Item()
,new com.groovifyingjava.optionaldatatyping.Item()
,new com.groovifyingjava.safelynavigate.Item()
,new com.groovifyingjava.simplifylistmapsetcreation.Item()
,new com.groovifyingjava.stringinterpolation.Item()
,new com.groovifyingjava.useelvisoperator.Item()
,new com.groovifyingjava.useequalsoperator.Item()
,new com.groovifyingjava.usemathoperators.Item()
,new com.groovifyingjava.useprintln.Item()
]
def overloadedConstructorArgs = [itemId: "14-101", manufacturer: "Raleigh", model: "Superbe Roadster", cost: 179.89, quantityOnHand: 10]
def overloadedConstructedClasses = [
new com.groovifyingjava.essentialgroovification.Item(overloadedConstructorArgs)
,new com.groovifyingjava.rewriteequals.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removesemicolons.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removeparentheses.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removeaccessors.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removeconstructors.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removeimports.Item(overloadedConstructorArgs)
,new com.groovifyingjava.removereturn.Item(overloadedConstructorArgs)
,new com.groovifyingjava.clarifyidentityequality.Item(overloadedConstructorArgs)
,new com.groovifyingjava.coercetypes.Item(overloadedConstructorArgs)
,new com.groovifyingjava.defaultaccessmodifiers.Item(overloadedConstructorArgs)
,new com.groovifyingjava.eachiteration.Item(overloadedConstructorArgs)
,new com.groovifyingjava.fieldaccessnotation.Item(overloadedConstructorArgs)
,new com.groovifyingjava.namedparameters.Item(overloadedConstructorArgs)
,new com.groovifyingjava.optionaldatatyping.Item(overloadedConstructorArgs)
,new com.groovifyingjava.safelynavigate.Item(overloadedConstructorArgs)
,new com.groovifyingjava.simplifylistmapsetcreation.Item(overloadedConstructorArgs)
,new com.groovifyingjava.stringinterpolation.Item(overloadedConstructorArgs)
,new com.groovifyingjava.useelvisoperator.Item(overloadedConstructorArgs)
,new com.groovifyingjava.useequalsoperator.Item(overloadedConstructorArgs)
,new com.groovifyingjava.usemathoperators.Item(overloadedConstructorArgs)
,new com.groovifyingjava.useprintln.Item(overloadedConstructorArgs)
]
@Test public void canCreateDefaultInstance() {
for(def item in defaultConstructedClasses) {
assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.itemId
assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.manufacturer
assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.model
assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.cost
assertEquals "Default construction of class ${item.class.name} failed to properly initialize field.", 0, item.quantityOnHand
assertEquals "Default construction of class ${item.class.name} failed to properly initialize field.", BigDecimal.ZERO, item.inventoryValue as BigDecimal
}
}
@Test public void canCreateInstancesFromOverloadedConstructor() {
for(def item in overloadedConstructedClasses) {
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.","14-101", item.itemId
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Raleigh", item.manufacturer
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Superbe Roadster", item.model
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 179.89, item.cost
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 10, item.quantityOnHand
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", new BigDecimal("1798.90"), item.inventoryValue as BigDecimal
}
}
}
This works well and is very easy to implement. It uses Groovy duck typing to test each class. The drawback is that junit console in Eclipse will on show the last test run. This is probably not a big deal. Also, it stops executing with the failed test. Note that I include the class name in the assert message strings. That makes it easy to identify the failing class.
The second version requires an abstract test class and have a test class in each package corresponding to each of the packages of the class under test. These become little more than stubs, but allow you to run tests discreetly or within a suite.
public abstract class CommonItemTest {
def overloadedConstructorArgs = [itemId: "14-101", manufacturer: "Raleigh", model: "Superbe Roadster", cost: 179.89, quantityOnHand: 10]
@Test public void canCreateDefaultInstance() {
def item = defaultConstructedClass
assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected itemId to be null.", item.itemId
assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected manufacturer to be null.", item.manufacturer
assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected model to be null.", item.model
assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected cost to be null.", item.cost
assertEquals "Default construction of class ${item.class.name} failed to properly initialize field. Expected quantityOnHand to be zero.", 0, item.quantityOnHand
assertEquals "Default construction of class ${item.class.name} failed to properly initialize field. Expected inventoryValue to be zero.", BigDecimal.ZERO, item.inventoryValue as BigDecimal
}
@Test public void canCreateInstancesFromOverloadedConstructor() {
def item = overloadedConstructedClass
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.","14-101", item.itemId
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Raleigh", item.manufacturer
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Superbe Roadster", item.model
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 179.89, item.cost
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 10, item.quantityOnHand
assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", new BigDecimal("1798.90"), item.inventoryValue as BigDecimal
}
abstract Object getDefaultConstructedClass();
abstract Object getOverloadedConstructedClass();
}
and the implementation...
public class ItemTest extends CommonItemTest {
public Object getDefaultConstructedClass() {
return new Item()
}
public Object getOverloadedConstructedClass() {
return new Item(overloadedConstructorArgs)
}
}
Upvotes: 0
Reputation: 114757
Of course. There is no technical limitation for that. But it's not advisable. There is a good reason that they called it unit test.
If you're about to use the same test for multiple classes, then this is usually a hint that you have duplicate code in your application.
Test classes are normal java classes so you could use inheritance to achieve what you have in mind: a base (test) class that contains the actual tests and sub (test-)classes that do some setup, iaw, initialize the tests with instances of the the different classes to be tested. You could even use reflection to create instances and method objects.
This could be a solution if you had to prepare unit tests for assignments where you expect dozens of different classes for the same task. But for most of all other cases I'd rather duplicate test methods then create complicated test classes.
Upvotes: 1
Reputation: 17769
You can using JUnit's @Parameterized.
@RunWith(Parameterized.class)
public class BehaviorTest {
@Parameters
public static Collection<Object[]> classesAndMethods() {
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{ Foo.class, Foo.class.getMethod("foo") });
return list;
}
private Class clazz;
private Method method;
public BehaviorTest(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
@Test
public void testBehavior() {
// Do stuff
}
}
Upvotes: 4
Reputation: 533442
How about something like...
for(Class cls: new Class[] { Class1.class, ... } )
assertEquals(1, nameOfMethod.invoke(cls.newInstance()));
Upvotes: 2