Reputation: 1890
To avoid alot of redundant test classes for simple integration testing, I'd like to create a parameterized generic test class like the following example:
@RunWith(Parameterized.class)
public class MovementTest<V extends Vehicle, T extends Track<V>> {
private final V testVehicle;
private final T testTrack;
public MovementTest(V vehicle, T track){
testVehicle = vehicle;
testTrack = track;
}
@Test
public void testMovement(){
testVehicle.moveAlong(testTrack);
}
@Parameters
public static Iterable<Object[]> provideTestExamples(){
Object[][] params = {
{ new Car(), new Highway() },
{ new Train(), new RailRoadTrack() }
};
return Arrays.asList(params);
}
}
public interface Vehicle {
void moveAlong(Track t);
}
public interface Track<E extends Vehicle> { }
public class Train implements Vehicle {
@Override
public void moveAlong(Track t) {}
}
public class RailRoadTrack implements Track<Train> {}
public class Car implements Vehicle {
@Override
public void moveAlong(Track t) { }
}
public class Highway implements Track<Car> {}
Unfortunately, this test class is not runnable. Is there a concise way to implement something alike?
Upvotes: 3
Views: 4636
Reputation: 350
I just solved a similar problem, a parameterized test with complex parameters (a combination of objects and lists of objects), and my search for help sent me here.
Both the code from the question and the code from the answer should roughly work. Taking in account that Object[][] will hold anything, the example with simpler types provides enough information. The only problem that may appear is to correctly declare the complex types.
I'm not running the tests with Eclipse but directly with Maven though.
Upvotes: 0
Reputation: 12122
You can use JUnit
's Parametrized
runner. It works as follows:
@RunWith(Parameterized.class)
public class ParametrizedTest {
private final String text;
private final int number;
public ParametrizedTest(String text, int number) {
this.text = text;
this.number = number;
}
@Test
public void shouldContainNumber() {
assertTrue(text.contains(String.valueOf(number)));
}
@Parameterized.Parameters
public static Iterable<Object[]> params() {
return Arrays.asList(
new Object[][]{
{"test string 1", 1},
{"test string 2", 2}
}
);
}
}
You can read more about this solution here
There's also better way (I think so) using JUnitParameters
(link), just take a look:
@RunWith(JUnitParamsRunner.class)
public class JUnitParamsTest{
@Test
@Parameters
public void shouldContainNumber(String text, int number) {
assertTrue(text.contains(String.valueOf(number)));
}
public Object[] parametersForShouldContainNumber() {
return $(
$("test string 1", 1),
$("test string 2", 2)
);
}
}
Note that name of method which supplies parameters has to fit test name. This solution seems better because (not only) you get better tests names after execution:
[OK] JUnitParams.[0] test string 1, 1 (shouldContainNumber)
[OK] JUnitParams.[1] test string 2, 2 (shouldContainNumber)
More comprehensive list of why it's better can be found at project site:
- more explicit - params are in test method params, not class fields
- less code - you don't need a constructor to set up parameters
- you can mix parametrised with non-parametrised methods in one class
- params can be passed as a CSV string or from a parameters provider class
- parameters provider class can have as many parameters providing methods as you want, so that you can group different cases
- you can have a test method that provides parameters (no external classes or statics anymore)
- you can see actual parameter values in your IDE (in JUnit's Parametrised it's only consecutive numbers of parameters)
Upvotes: 2