Parameterized test case classes in JUnit 3.x - java

I have a JUnit 3.x TestCase which I would like to be able to parameterize. I'd like to parametrize the entire TestCase (including the fixture). However, the TestSuite.addTestSuite() method does not allow be to pass a TestCase object, just a class:
TestSuite suite = new TestSuite("suite");
suite.addTestSuite(MyTestCase.class);
I would like to be able to pass a parameter (a string) to the MyTestCase instance which is created when the test runs. As it is now, I have to have a separate class for each parameter value.
I tried passing it an anynomous subclass:
MyTestCase testCase = new MyTestCase() {
String getOption() {
return "some value";
}
}
suite.addTestSuite(testCase.getClass());
However, this fails with the assertion:
... MyTestSuite$1 has no public constructor TestCase(String name) or TestCase()`
Any ideas? Am I attacking the problem the wrong way?

If this is Java 5 or higher, you might want to consider switching to JUnit 4, which has support for parameterized test cases built in.

Rather than create a parameterized test case for the multiple/different backends you want to test against, I would look into making my test cases abstract. Each new implementation of your API would need to supply an implementing TestCase class.
If you currently have a test method that looks something like
public void testSomething() {
API myAPI = new BlahAPI();
assertNotNull(myAPI.something());
}
just add an abstract method to the 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 the TestCase for the new implementation you want to test only has to implement your AbstractTestCase and supply the concrete implementation of the API class:
public class ImplementationXTestCase extends AbstractTestCase{
public API getAPIToTest() {
return new ImplementationX();
}
}
Then all of the test methods that test the API in the abstract class are run automatically.

Ok, here is a quick mock-up of how JUnit 4 runs parameterized tests, but done in JUnit 3.8.2.
Basically I'm subclassing and badly hijacking the TestSuite class to populate the list of tests according to the cross-product of testMethods and parameters.
Unfortunately I've had to copy a couple of helper methods from TestSuite itself, and a few details are not perfect, such as the names of the tests in the IDE being the same across parameter sets (JUnit 4.x appends [0], [1], ...).
Nevertheless, this seems to run fine in the text and AWT TestRunners that ship with JUnit as well as in Eclipse.
Here is the ParameterizedTestSuite, and further down a (silly) example of a parameterized test using it.
(final note : I've written this with Java 5 in mind, it should be trivial to adapt to 1.4 if needed)
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 just here to show that all test instances are different as they should be, and that there is no shared state between them.

a few details are not perfect, such as the names of the tests in the IDE being the same across parameter sets (JUnit 4.x appends [0], [1], ...).
To solve this you just need to overwrite getName() and change the constructor in your 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 + "]";
}

For Android projects, we wrote a library called Burst for test parameterization. For example
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 really an answer to your question since you're not using Android, but a lot of projects which still use JUnit 3 do so because Android's test framework requires it, so I hope some other readers will find this helpful.

Related

Parameterized testcontainer image tag with Junit5

In the Testcontainers documentation, there is an example for having the docker image to be parameterized with #ParameterizedTest.
This was a junit4 example.
https://github.com/testcontainers/testcontainers-java/blob/main/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java
#RunWith(Parameterized.class)
public class ParameterizedDockerfileContainerTest {
private final String expectedVersion;
#Rule
public GenericContainer container;
public ParameterizedDockerfileContainerTest(String baseImage, String expectedVersion) {
container =
new GenericContainer(
new ImageFromDockerfile()
.withDockerfileFromBuilder(builder -> {
builder
.from(baseImage)
// Could potentially customise the image here, e.g. adding files, running
// commands, etc.
.build();
})
)
.withCommand("top");
this.expectedVersion = expectedVersion;
}
#Parameterized.Parameters(name = "{0}")
public static Object[][] data() {
return new Object[][] { //
{ "alpine:3.12", "3.12" },
{ "alpine:3.13", "3.13" },
{ "alpine:3.14", "3.14" },
{ "alpine:3.15", "3.15" },
{ "alpine:3.16", "3.16" },
};
}
#Test
public void simpleTest() throws Exception {
final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout();
assertThat(release).as("/etc/alpine-release starts with " + expectedVersion).startsWith(expectedVersion);
}
}
I couldn't find a way to do something similar with junit5, basically :
having the container only started once for all the #ParameterizedTest methods in the class
Ofc, with a lot of if/else, playing with beforeEach, TestInfo, ... is possible but I feel like something is wrong and I'm sure the following question has probably should be answered with junit5
How to use parameterized tests for testing with multiple database versions
So, it seems the equivalent to junit5 is not possible, related to this opened issue https://github.com/junit-team/junit5/issues/878#issuecomment-546459081 as mentioned by Eddù above
With the great help of Michael Simons, I could manage to do something that works
Base class :
#Testcontainers(disabledWithoutDocker = true)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class MultipleNeo4jVersionsTest {
protected static String HEAP_SIZE = "256M";
public static Stream<String> neo4jVersions() {
return Stream.of("4.4.14", "5.2.0");
}
protected static String heapSizeSetting(Neo4jVersion version) {
return version.equals(Neo4jVersion.V4_4)
? "NEO4J_dbms_memory_heap_max__size"
: "NEO4J_server_memory_heap_max__size"
;
}
protected Neo4jContainer<?> getNeo4j(String version) {
var imageName = String.format("neo4j:%s-enterprise", version);
Neo4jVersion neo4jVersion = Neo4jVersion.of(version);
Neo4jContainer<?> container = new Neo4jContainer<>(imageName)
.withoutAuthentication()
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
.withEnv(heapSizeSetting(neo4jVersion), HEAP_SIZE)
.withReuse(true);
container.start();
return container;
}
}
And the actual test class
#ParameterizedTest
#MethodSource("neo4jVersions")
void loading_config(String version) throws Exception {
Neo4jContainer<?> neo4j = getNeo4j(version);
// do something here
}
Couple of useful links
https://foojay.io/today/faster-integration-tests-with-reusable-testcontainers/
https://github.com/michael-simons/neo4j-migrations/blob/main/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/CatalogBasedMigrationIT.java
https://github.com/michael-simons/neo4j-migrations/blob/main/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/TestBase.java#L49
Devopology Test Engine (Junit5 based) supports parameterized class testing. (I'm the author)
https://github.com/devopology/test-engine
Your example...
package test.neo4j;
import org.devopology.test.engine.api.AfterAll;
import org.devopology.test.engine.api.BeforeAll;
import org.devopology.test.engine.api.Parameter;
import org.devopology.test.engine.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import java.util.ArrayList;
import java.util.Collection;
public class Neo4jTest {
#Parameter
public String dockerImageName;
#Parameter.Supplier
public static Collection<String> dockerImageNames() {
Collection<String> dockerImageNames = new ArrayList<>();
dockerImageNames.add("neo4j:4.4.14-enterprise");
dockerImageNames.add("neo4j:5.2.0-enterprise");
return dockerImageNames;
}
private Neo4jContainer<?> neo4jContainer;
#BeforeAll
public void beforeAll() {
neo4jContainer = new Neo4jContainer<>(dockerImageName)
.waitingFor(Wait.forLogMessage(".*Started..*", 1))
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
.withEnv(heapSizeSetting(dockerImageName), "256M")
.withLogConsumer(outputFrame -> System.out.print(outputFrame.getUtf8String()))
.withoutAuthentication();
neo4jContainer.start();
}
#Test
public void test1() {
System.out.println("test1 : dockerImageName = [" + dockerImageName + "]");
// do something here
}
#Test
public void test2() {
System.out.println("test2 : dockerImageName = [" + dockerImageName + "]");
// do something here
}
#AfterAll
public void afterAll() {
if (neo4jContainer != null) {
try {
neo4jContainer.close();
} catch (Throwable t) {
// DO NOTHING
}
neo4jContainer = null;
}
}
protected static String heapSizeSetting(String dockerImageName) {
return dockerImageName.contains("4.4")
? "NEO4J_dbms_memory_heap_max__size" : "NEO4J_server_memory_heap_max__size";
}
}

Junit - how Java deals with class methods having Asserts

I a quite new to Junit. I have a question on how Java runs Junit classes. I have a code like this
public class TestJunit1 {
String message = "Srinivas";
MessageUtil messageutil = new MessageUtil(message);
#Test
public void testPrintMessage() {
System.out.println("Inside testPrintMessage");
assertEquals(message, messageutil.printMessage());
}
}
public class TestJUnit5 extends TestCase {
protected double fValue1;
protected double fValue2;
#Before
public void setUp() throws Exception {
fValue1 = 2.0;
fValue2 = 3.0;
}
#Test
public void testAdding() {
System.out.println("No of test cases =" + this.countTestCases());
String name = this.getName();
System.out.println("Test Case name is: "+name);
this.setName("methodNewAdd");
String newName = this.getName();
System.out.println("New name of the test case is:"+newName);
System.out.println(this.getClass());
}
#After
public void tearDown() throws Exception {
}
public class TestSuiteDemo {
public static void main(String[] args) {
TestSuite ts = new TestSuite(TestJunit1.class, TestJunit2.class, TestJUnit5.class);
TestResult tr = new TestResult();
ts.run(tr);
System.out.println("Number of test cases is:"+tr.runCount());
}
}
when I run TestSuiteDemo as Java application in eclipse, it produces output from println statements from TestJUnit5 and not from TestJunit1.
Could somebody please explain why this happens?
Regards
Srinivas
Because TestJUnit5 extends a TestCase (not TestJunit1) so JUnit sees the test case and runs it.
From the junit.framework.TestSuite code, any class that inherits Test class or extends TestCase can get registered with TestSuite constructor.
private void addTestsFromTestCase(final Class<?> theClass) {
fName = theClass.getName();
try {
getTestConstructor(theClass); // Avoid generating multiple error messages
} catch (NoSuchMethodException e) {
addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
return;
}
if (!Modifier.isPublic(theClass.getModifiers())) {
addTest(warning("Class " + theClass.getName() + " is not public"));
return;
}
Class<?> superClass = theClass;
List<String> names = new ArrayList<String>();
while (Test.class.isAssignableFrom(superClass)) {
for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
addTestMethod(each, names, theClass);
}
superClass = superClass.getSuperclass();
}
if (fTests.size() == 0) {
addTest(warning("No tests found in " + theClass.getName()));
}
}
To get TestJunit1 to work, you will have to create an annotative Suite.
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Suite.class)
#Suite.SuiteClasses({
TestJunit1.class,
TestJunit2.class,
TestJunit5.class
})
public class JunitTestSuite {
}
And to run it locally:
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
public class JunitTestSuiteRunner {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JunitTestSuite.class);
for (Failure fail : result.getFailures()) {
System.out.println(fail.toString());
}
if (result.wasSuccessful()) {
System.out.println("All tests finished successfully...");
}
}
}
I hope this helps.
Source reference.
amoq reference to the stackoverflow post JUnit confusion: use 'extends TestCase' or '#Test'?
is accurate. There are two main way for junit test to recognize and run your rest Junit version 3 , extend test case . Junit version 4 annotate with #Test.
Since your test 1 has neither it is not run at all. Confirm the version of junit your eclipse is using, you could use the annotation version #Test which u might find more pleasant

Parameterized unit test suites

I am trying to set up some parameterized test suites, unfortunately without any luck so far.
I have two set of parameters, and I would like to run multiple test cases (they are in different classes) with all possible combinations. I tried to do it with JUnit4, but I am unable to set it up correctly. This would be my basic idea:
TestSuite1.class sets up one set of parameters, then it starts TestSuite2.class.
TestSuite2.class sets up the second set of parameters, then it starts the actual test(s) that will use both parameters.
Meanwhile it seems it is not possible to set up both Suite.class and Parameterized.class in the RunWith annotation at the same time (according to google, Parameterized extends Suite, I get usually "no runnable method found" message if I use.)
This is how my code looks like basically:
TestSuite1.class:
#RunWith(Parameterized.class)
#Parameterized.SuiteClasses({TestSuite2.class})
//I have tried with #RunWith(Suite.class) and
//#Suite.SuiteClasses({TestSuite2.class}) annotations also - all combinations
public class TestSuite1{
public TestSuite1(int number) {
Params.first = number;
}
#Parameters
public static Collection<Object[]> parameters(){
Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };
return Arrays.asList(data);
}
}
TestSuite2.class looks the same as TestSuite1.class, except that I have added TestCase1.class to the suite instead of TestSuite2, and that it sets another variable in Params.
TestCase1.class:
public class TestCase1 {
#Test
public void test1(){
System.out.println("first: "+Params.first+" second: "+Params.second);
Assert.assertTrue(true);
}
}
I am open to all ideas - even with TestNG for example. I have tried it also (although today was the first time I saw it), but as I noticed the suites are a bit different than in JUnit. I would prefer not to set up XML files before testing, I would like to solve all set up programmatically.
Is what I am trying to achieve possible with any framework?
Update: With TestNG I have the following code:
Start.class:
public class Start {
public static void main(String[] args){
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { FirstTest.class, SecondTest.class });
testng.addListener(tla);
testng.run();
}
}
Params.class:
public class Params {
#DataProvider(name = "param")
public static Object[][] createData() {
Object[][] data = new Object[][] { { 1 }, { 2}, { 3}, { 4} };
return data;
}
}
FirstTest.class:
public class FirstTest {
#Test(dataProvider = "param", dataProviderClass = Params.class)
public static void printIt(int number){
System.out.println("FirstTest: "+number);
}
}
SecondTest.class is the same as FirstTest.class. If I run this, it runs FirstTest 4 times, then it runs SecondTest 4 times. I would like to run FirstTest one time, and SecondTest one time also with the first set of parameters. Then I would like to run FirstTest and SecondTest one time, with the second set of parameters, etc.
I have tried to set setPreserveOrder(true), and tried all setParallel options also. On this way however the results are in kind of random order.
(It would be some selenium test. I am aware that tests should not depend on each other, but still it would be my desired way for this)
Although Parameterized extends Suite, it behaves totally different - in disrespect of the Liskov substitution principle. This is because normally the constructor Suite(Class<?>, RunnerBuilder) processes the #SuiteClasses annotation. But Parameterized(Class<?>) replaces this behaviour with a processing of #Parameters.
If you want to combine the behaviour of Suite and Parameterized you have to look outside of JUnit 4. E.g. you could implement your own Runner like Adam Hawkes already mentioned in another post here.
I did the same by myself and cobbled a library together that provides you with a ParameterizedSuite Runner: https://github.com/PeterWippermann/parameterized-suite
A parameterized test suite looks like this:
#RunWith(ParameterizedSuite.class)
#SuiteClasses({OneTest.class, TwoTest.class})
public class MyParameterizedTestSuite {
#Parameters(name = "Parameters are {0} and {1}")
public static Object[] params() {
return new Object[][] {{'A',1}, {'B',2}, {'C',3}};
}
So basicly as far as I understand, what you want to do is to run a test with set of sets of parameters. That is possible with JUnit and that is why method annotated with #Parameters returns a Collection of arrays (in general a set of sets).
Look at this example:
import static org.junit.runners.Parameterized.Parameters;
#RunWith(Parameterized.class)
public class TestCase1 {
public TestCase1(int first, int second) {
Params.first = first;
Params.second = second;
}
#Parameters
public static Collection<Object[]> parameters(){
Object[][] data = new Object[][] { { 1, 11 }, { 2, 22 }, { 3, 33 }, { 4, 44 } };
return Arrays.asList(data);
}
#Test
public void test1(){
System.out.println("first: "+Params.first+" second: "+Params.second);
Assert.assertTrue(true);
}
}
Edit:
If you want to share parameters between multiple test you could use an abstraction in your test cases.
public class AbstractParametrizedTest {
public AbstractParametrizedTest(int first, int second) {
Params.first = first;
Params.second = second;
}
#Parameterized.Parameters
public static Collection<Object[]> parameters(){
Object[][] data = new Object[][] { { 1, 11 }, { 2, 22 }, { 3, 33 }, { 4, 44 } };
return Arrays.asList(data);
}
}
#RunWith(Parameterized.class)
public class TestCase1 extends AbstractParametrizedTest {
public TestCase1(int first, int second) {
super(first, second);
}
...
}
However the best way to do it i think, would be to use TestNGs data providers. Take a look at example at section 5.6.2 and usage of static data providers
http://testng.org/doc/documentation-main.html
To achieve the goal of executing all test cases with the same parameters in sequential order, you would need a different Runner as this behavior is held in that class. You're in luck, as this is available in the JUnit Toolbox Project with the ParallelParameterized class!
Here some other suggest that seems to be much more flexible: #RunWith(Enclosed.class)
In short:
Instead of #Suite.SuiteClasses(...), just use #RunWith(Enclosed.class) and extend your Test Classes
#RunWith(Enclosed.class)
public class FastTest {
public static class Test1FirstAppInit extends AppInitTest { }
public static class Test2Download extends DownloadTest{ }
public static class Test3OtherTest extends OtherTest { }
}
Now with Parameterized:
#RunWith(Enclosed.class)
public class FastTest {
private static Iterable<? extends Object> mAllLocale = Arrays.asList(Locale.ENGLISH, Locale.GERMAN);
private static Iterable<? extends Object> mSingleLocale = Arrays.asList(Locale.ENGLISH);
/*
Run test class for all Locale
*/
#RunWith(Parameterized.class)
public static class Test1FirstAppInit extends AppInitTest {
#Parameterized.Parameter
public Locale mLocale;
#Parameterized.Parameters
public static Iterable<? extends Object> data() {
return mAllLocale;
}
#Override
public Locale getLocale() {
return mLocale;
}
#Override
public void test001ResetAll {
assumeTrue(false); // skip test completedly
}
#Override
public void test002ClearAppData() {
// replace existing test
if (getLocale() != Locale.ENGLISH) {
/*
should run only on first Locale
skip test on following Parameter runs
*/
assumeTrue(false); // skip test
}
else {
super.test000ClearAppData();
}
}
}
/*
Run test class only for one Locale
*/
#RunWith(Parameterized.class)
public static class Test2Download extends DownloadTest{
#Parameterized.Parameter
public Locale mLocale;
#Parameterized.Parameters
public static Iterable<? extends Object> data(){
return mSingleLocale;
}
#Override
public Locale getLocale() {
return mLocale;
}
#Override
public void test900Delete() {
assumeTrue(false); // skip test
}
}
/*
Test not Parameterized
*/
public static class Test3OtherTest extends OtherTest { }
}
Your Test Classes for Parameterized tests look like this:
#RunWith(AndroidJUnit4.class)
#LargeTest
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DownloadTest {
public Locale getLocale() {
// will be overwritten in #RunWith(Enclosed.class)
// but we are still able to run test class separatedly
return Locale.ENGLISH;
}
#Test
public void test900Delete() {
....
}
....
}
Matches exactly what I was searching for. I can create different Test scenarios (full test, fast test,...). Just create different #RunWith(Enclosed.class) classes and extend the tests that you want to include.
Only side point seems to be that Enclosed.class does not care about sort order (if important to you).
I solved it by replacing Enclosed:
public class SortedEnclosed extends Suite {
public SortedEnclosed(Class<?> klass, RunnerBuilder builder) throws Throwable {
super(builder, klass, filterAbstractClasses(klass.getClasses()));
}
protected static Class<?>[] filterAbstractClasses(final Class<?>[] classes) {
final List<Class<?>> filteredList= new ArrayList<Class<?>>(classes.length);
for (final Class<?> clazz : classes) {
if (!Modifier.isAbstract(clazz.getModifiers())) {
filteredList.add(clazz);
}
}
// this is new (there may be better way with own "#FixClassOrder"...):
Collections.sort(filteredList, new Comparator<Class<?>>() {
#Override
public int compare(Class<?> o1, Class<?> o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
});
//
return filteredList.toArray(new Class<?>[filteredList.size()]);
}
}
And then use #RunWith(SortedEnclosed.class)

How to group JUnit tests together

I'm trying to create a junit Runner that will group together common tests using the junit API:
package whatever;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
public class SomeTestRunner extends Runner {
public SomeTestRunner(Class<?> testClass) {}
#Override
public Description getDescription() {
return Description.EMPTY;
}
#Override
public void run(RunNotifier notifier) {
for (int i = 0; i < 3; i++) {
Description parent = Description.createSuiteDescription("Parent_" + i);
for (int j = 0; j < 3; j++) {
Description child = Description.createTestDescription(Exception.class, "Child_" + j);
parent.addChild(child);
Failure failure = new Failure(child, new Exception());
notifier.fireTestFailure(failure);
}
Failure failure = new Failure(parent, new Exception());
notifier.fireTestFailure(failure);
}
}
}
The problem is, when I run a test using this Runner, I can see the failures in a row, both parents and children, instead of grouped together:
Results :
Tests in error:
Child_0(java.lang.Exception)
Child_1(java.lang.Exception)
Child_2(java.lang.Exception)
Parent_0
Child_0(java.lang.Exception)
Child_1(java.lang.Exception)
Child_2(java.lang.Exception)
Parent_1
Child_0(java.lang.Exception)
Child_1(java.lang.Exception)
Child_2(java.lang.Exception)
Parent_2
Tests run: 12, Failures: 0, Errors: 12, Skipped: 0
Also, I expected to see them grouped together when I run this test in Eclipse - but thats not the case. What am I missing? Is it even possible?
You can use a JUnit Test Suite, just like this:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(Suite.class)
#SuiteClasses({ SomeTest.class, AnotherTest.class, YetAnotherTest.class })
public class AllTests {
}
The template to follow is Parameterized, because it seems to do what you want. For a given test class it runs the test methods multiple times, it creates a Runner for each set of parameters, which is what I think you want:
public class GroupedTestRunner extends Suite {
private class TestClassRunnerForParameters extends BlockJUnit4ClassRunner {
private String name;
TestClassRunnerForParameters(Class<?> type, String name) throws InitializationError {
super(type);
this.name = name;
}
#Override
public Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance();
}
#Override
protected String getName() {
return String.format("[%s]", name);
}
#Override
protected String testName(final FrameworkMethod method) {
return String.format("%s[%s]", method.getName(), name);
}
}
private final ArrayList<Runner> runners = new ArrayList<Runner>();
public GroupedTestRunner(Class<?> klass) throws Throwable {
super(klass, Collections.<Runner> emptyList());
// do grouping things here
runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(), "group1"));
runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(), "group2"));
}
#Override
protected List<Runner> getChildren() {
return runners;
}
}
This produces output like (in Eclipse):

Using different classloaders for different JUnit tests?

I have a Singleton/Factory object that I'd like to write a JUnit test for. The Factory method decides which implementing class to instantiate based upon a classname in a properties file on the classpath. If no properties file is found, or the properties file does not contain the classname key, then the class will instantiate a default implementing class.
Since the factory keeps a static instance of the Singleton to use once it has been instantiated, to be able to test the "failover" logic in the Factory method I would need to run each test method in a different classloader.
Is there any way with JUnit (or with another unit testing package) to do this?
edit: here is some of the Factory code that is in use:
private static MyClass myClassImpl = instantiateMyClass();
private static MyClass instantiateMyClass() {
MyClass newMyClass = null;
String className = null;
try {
Properties props = getProperties();
className = props.getProperty(PROPERTY_CLASSNAME_KEY);
if (className == null) {
log.warn("instantiateMyClass: Property [" + PROPERTY_CLASSNAME_KEY
+ "] not found in properties, using default MyClass class [" + DEFAULT_CLASSNAME + "]");
className = DEFAULT_CLASSNAME;
}
Class MyClassClass = Class.forName(className);
Object MyClassObj = MyClassClass.newInstance();
if (MyClassObj instanceof MyClass) {
newMyClass = (MyClass) MyClassObj;
}
}
catch (...) {
...
}
return newMyClass;
}
private static Properties getProperties() throws IOException {
Properties props = new Properties();
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_FILENAME);
if (stream != null) {
props.load(stream);
}
else {
log.error("getProperties: could not load properties file [" + PROPERTIES_FILENAME + "] from classpath, file not found");
}
return props;
}
This question might be old but since this was the nearest answer I found when I had this problem I though I'd describe my solution.
Using JUnit 4
Split your tests up so that there is one test method per class (this solution only changes classloaders between classes, not between methods as the parent runner gathers all the methods once per class)
Add the #RunWith(SeparateClassloaderTestRunner.class) annotation to your test classes.
Create the SeparateClassloaderTestRunner to look like this:
public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner {
public SeparateClassloaderTestRunner(Class<?> clazz) throws InitializationError {
super(getFromTestClassloader(clazz));
}
private static Class<?> getFromTestClassloader(Class<?> clazz) throws InitializationError {
try {
ClassLoader testClassLoader = new TestClassLoader();
return Class.forName(clazz.getName(), true, testClassLoader);
} catch (ClassNotFoundException e) {
throw new InitializationError(e);
}
}
public static class TestClassLoader extends URLClassLoader {
public TestClassLoader() {
super(((URLClassLoader)getSystemClassLoader()).getURLs());
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("org.mypackages.")) {
return super.findClass(name);
}
return super.loadClass(name);
}
}
}
Note I had to do this to test code running in a legacy framework which I couldn't change. Given the choice I'd reduce the use of statics and/or put test hooks in to allow the system to be reset. It may not be pretty but it allows me to test an awful lot of code that would be difficult otherwise.
Also this solution breaks anything else that relies on classloading tricks such as Mockito.
When I run into these sort of situations I prefer to use what is a bit of a hack. I might instead expose a protected method such as reinitialize(), then invoke this from the test to effectively set the factory back to its initial state. This method only exists for the test cases, and I document it as such.
It is a bit of a hack, but it's a lot easier than other options and you won't need a 3rd party lib to do it (though if you prefer a cleaner solution, there probably are some kind of 3rd party tools out there you could use).
You can use Reflection to set myClassImpl by calling instantiateMyClass() again. Take a look at this answer to see example patterns for playing around with private methods and variables.
If executing Junit via the Ant task you can set fork=true to execute every class of tests in it's own JVM. Also put each test method in its own class and they will each load and initialise their own version of MyClass. It's extreme but very effective.
Below you can find a sample that does not need a separate JUnit test runner and works also with classloading tricks such as Mockito.
package com.mycompany.app;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.net.URLClassLoader;
import org.junit.Test;
public class ApplicationInSeparateClassLoaderTest {
#Test
public void testApplicationInSeparateClassLoader1() throws Exception {
testApplicationInSeparateClassLoader();
}
#Test
public void testApplicationInSeparateClassLoader2() throws Exception {
testApplicationInSeparateClassLoader();
}
private void testApplicationInSeparateClassLoader() throws Exception {
//run application code in separate class loader in order to isolate static state between test runs
Runnable runnable = mock(Runnable.class);
//set up your mock object expectations here, if needed
InterfaceToApplicationDependentCode tester = makeCodeToRunInSeparateClassLoader(
"com.mycompany.app", InterfaceToApplicationDependentCode.class, CodeToRunInApplicationClassLoader.class);
//if you want to try the code without class loader isolation, comment out above line and comment in the line below
//CodeToRunInApplicationClassLoader tester = new CodeToRunInApplicationClassLoaderImpl();
tester.testTheCode(runnable);
verify(runnable).run();
assertEquals("should be one invocation!", 1, tester.getNumOfInvocations());
}
/**
* Create a new class loader for loading application-dependent code and return an instance of that.
*/
#SuppressWarnings("unchecked")
private <I, T> I makeCodeToRunInSeparateClassLoader(
String packageName, Class<I> testCodeInterfaceClass, Class<T> testCodeImplClass) throws Exception {
TestApplicationClassLoader cl = new TestApplicationClassLoader(
packageName, getClass(), testCodeInterfaceClass);
Class<?> testerClass = cl.loadClass(testCodeImplClass.getName());
return (I) testerClass.newInstance();
}
/**
* Bridge interface, implemented by code that should be run in application class loader.
* This interface is loaded by the same class loader as the unit test class, so
* we can call the application-dependent code without need for reflection.
*/
public static interface InterfaceToApplicationDependentCode {
void testTheCode(Runnable run);
int getNumOfInvocations();
}
/**
* Test-specific code to call application-dependent code. This class is loaded by
* the same class loader as the application code.
*/
public static class CodeToRunInApplicationClassLoader implements InterfaceToApplicationDependentCode {
private static int numOfInvocations = 0;
#Override
public void testTheCode(Runnable runnable) {
numOfInvocations++;
runnable.run();
}
#Override
public int getNumOfInvocations() {
return numOfInvocations;
}
}
/**
* Loads application classes in separate class loader from test classes.
*/
private static class TestApplicationClassLoader extends URLClassLoader {
private final String appPackage;
private final String mainTestClassName;
private final String[] testSupportClassNames;
public TestApplicationClassLoader(String appPackage, Class<?> mainTestClass, Class<?>... testSupportClasses) {
super(((URLClassLoader) getSystemClassLoader()).getURLs());
this.appPackage = appPackage;
this.mainTestClassName = mainTestClass.getName();
this.testSupportClassNames = convertClassesToStrings(testSupportClasses);
}
private String[] convertClassesToStrings(Class<?>[] classes) {
String[] results = new String[classes.length];
for (int i = 0; i < classes.length; i++) {
results[i] = classes[i].getName();
}
return results;
}
#Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
if (isApplicationClass(className)) {
//look for class only in local class loader
return super.findClass(className);
}
//look for class in parent class loader first and only then in local class loader
return super.loadClass(className);
}
private boolean isApplicationClass(String className) {
if (mainTestClassName.equals(className)) {
return false;
}
for (int i = 0; i < testSupportClassNames.length; i++) {
if (testSupportClassNames[i].equals(className)) {
return false;
}
}
return className.startsWith(appPackage);
}
}
}

Categories