Parameterized Test Classes in JUnit 3.x

I have a JUnit 3.x TestCase that I would like to be able to parameterize. I would like to parameterize the entire TestCase (including the instrument). However, the TestSuite.addTestSuite() method does not allow passing a TestCase object, just a class:

  TestSuite suite = new TestSuite("suite"); suite.addTestSuite(MyTestCase.class); 

I would like to pass the parameter (string) to the MyTestCase instance that is created when the test runs. As now, I must have a separate class for each parameter value.

I tried to pass some subclass to it:

  MyTestCase testCase = new MyTestCase() { String getOption() { return "some value"; } } suite.addTestSuite(testCase.getClass()); 

However, this fails with the statement:

  ... MyTestSuite$1 has no public constructor TestCase(String name) or TestCase()` 

Any ideas? Am I attacking a problem wrong?

+4
source share
5 answers

If it is Java 5 or higher, you may need to switch to JUnit 4, which supports built-in built-in test cases.

+2
source

Instead of creating a parameterized test case for several / different backends with which you want to test, I would consider abstract theses. Each new implementation of your API must provide an implementation of the TestCase class.

If you have a testing method that looks something like

 public void testSomething() { API myAPI = new BlahAPI(); assertNotNull(myAPI.something()); } 

just add an abstract method to TestCase that returns the specific API object to use.

 public abstract class AbstractTestCase extends TestCase { public abstract API getAPIToTest(); public void testSomething() { API myAPI = getAPIToTest(); assertNotNull(myAPI.something()); } public void testSomethingElse() { API myAPI = getAPIToTest(); assertNotNull(myAPI.somethingElse()); } } 

Then TestCase for the new implementation you want to test should implement your AbstractTestCase and provide a specific implementation of the API class:

 public class ImplementationXTestCase extends AbstractTestCase{ public API getAPIToTest() { return new ImplementationX(); } } 

Then, all test methods that test the APIs in the abstract class are launched automatically.

+3
source

Ok, here is a quick layout of how JUnit 4 runs parameterized tests, but runs in JUnit 3.8.2.

I basically subclass and badly grab the TestSuite class to populate the list of tests according to the cross product testMethods and parameters.

Unfortunately, I had to copy several helper methods from TestSuite itself, and some details are not ideal, for example, the test names in the IDE are the same for parameter sets (JUnit 4.x appends [0] , [1] , ...).

However, this seems to work well in the text and in the AWT TestRunner that come with JUnit as well as in Eclipse.

Here is the ParameterizedTestSuite parameter and further down the (silly) example of a parameterized test using it.

(final note: I wrote this using Java 5 in mind, it should be trivial to adapt to 1.4 if necessary)

ParameterizedTestSuite.java:

 package junit.parameterized; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; public class ParameterizedTestSuite extends TestSuite { public ParameterizedTestSuite( final Class<? extends TestCase> testCaseClass, final Collection<Object[]> parameters) { setName(testCaseClass.getName()); final Constructor<?>[] constructors = testCaseClass.getConstructors(); if (constructors.length != 1) { addTest(warning(testCaseClass.getName() + " must have a single public constructor.")); return; } final Collection<String> names = getTestMethods(testCaseClass); final Constructor<?> constructor = constructors[0]; final Collection<TestCase> testCaseInstances = new ArrayList<TestCase>(); try { for (final Object[] objects : parameters) { for (final String name : names) { TestCase testCase = (TestCase) constructor.newInstance(objects); testCase.setName(name); testCaseInstances.add(testCase); } } } catch (IllegalArgumentException e) { addConstructionException(e); return; } catch (InstantiationException e) { addConstructionException(e); return; } catch (IllegalAccessException e) { addConstructionException(e); return; } catch (InvocationTargetException e) { addConstructionException(e); return; } for (final TestCase testCase : testCaseInstances) { addTest(testCase); } } private Collection<String> getTestMethods( final Class<? extends TestCase> testCaseClass) { Class<?> superClass= testCaseClass; final Collection<String> names= new ArrayList<String>(); while (Test.class.isAssignableFrom(superClass)) { Method[] methods= superClass.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { addTestMethod(methods[i], names, testCaseClass); } superClass = superClass.getSuperclass(); } return names; } private void addTestMethod(Method m, Collection<String> names, Class<?> theClass) { String name= m.getName(); if (names.contains(name)) return; if (! isPublicTestMethod(m)) { if (isTestMethod(m)) addTest(warning("Test method isn't public: "+m.getName())); return; } names.add(name); } private boolean isPublicTestMethod(Method m) { return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); } private boolean isTestMethod(Method m) { String name= m.getName(); Class<?>[] parameters= m.getParameterTypes(); Class<?> returnType= m.getReturnType(); return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE); } private void addConstructionException(Exception e) { addTest(warning("Instantiation of a testCase failed " + e.getClass().getName() + " " + e.getMessage())); } } 

ParameterizedTest.java:

 package junit.parameterized; import java.util.Arrays; import java.util.Collection; import junit.framework.Test; import junit.framework.TestCase; import junit.parameterized.ParameterizedTestSuite; public class ParameterizedTest extends TestCase { private final int value; private int evilState; public static Collection<Object[]> parameters() { return Arrays.asList( new Object[] { 1 }, new Object[] { 2 }, new Object[] { -2 } ); } public ParameterizedTest(final int value) { this.value = value; } public void testMathPow() { final int square = value * value; final int powSquare = (int) Math.pow(value, 2) + evilState; assertEquals(square, powSquare); evilState++; } public void testIntDiv() { final int div = value / value; assertEquals(1, div); } public static Test suite() { return new ParameterizedTestSuite(ParameterizedTest.class, parameters()); } } 

Note: the evilState variable is here to show that all test instances are different, as it should be, and that there is no common state between them.

+3
source

several details are not ideal, for example, test names in the IDE are the same for parameter sets (JUnit 4.x adds [0], [1], ...).

To solve this problem, you just need to overwrite getName () and change the constructor in the test case class:

  private String displayName; public ParameterizedTest(final int value) { this.value = value; this.displayName = Integer.toString(value); } @Override public String getName() { return super.getName() + "[" + displayName + "]"; } 
+1
source

For Android projects, we wrote a library called Burst to parameterize the test. for instance

 public class ParameterizedTest extends TestCase { enum Drink { COKE, PEPSI, RC_COLA } private final Drink drink; // Nullary constructor required by Android test framework public ConstructorTest() { this(null); } public ConstructorTest(Drink drink) { this.drink = drink; } public void testSomething() { assertNotNull(drink); } } 

Not quite the answer to your question, since you are not using Android, but many projects that still use JUnit 3 do this because it requires the Android platform, so I hope some other readers find this useful.

+1
source

Source: https://habr.com/ru/post/1277695/


All Articles