I'm trying to test implementations of a Collections
interface using JUnit4 Parameterized
tests. My test class has two tests:
@RunWith(Parameterized.class)
public class CollectionsTest {
private Collection<String> col;
private Collection<String> other;
public CollectionsTest(Collection<String> c, Collection<String> other) {
this.col = c;
this.other = other;
}
@Parameterized.Parameters
public static java.util.Collection<Object[]> tokenStreams() {
return (java.util.Collection<Object[]>) Arrays.asList(new Object[][] {
{ new DSLinkedList<String>(), new DSLinkedList<String>() } });
}
@Test
public final void isEmpty() {
assertTrue(col.getClass().getName() + ".isEmpty() should return true when collection contains no elements", col.isEmpty());
col.add("Stringthing");
assertFalse(col.getClass().getName() + ".isEmpty() should return false when collection contains elements", col.isEmpty());
}
@Test
public final void size() {
assertEquals(col.getClass().getName() + ".size() should be 0 for an empty collection.", 0, col.size());
col.add("String");
assertEquals(col.getClass().getName() + ".size() should be 1 for a collection with one element.", 1, col.size());
}
}
The second test (size()
) always fails: at the time of the first assertion, col
contains a single element stringThing
, because I inserted an element in the isEmpty()
test.
If I wasn't using a parameterized test, I'd use @Before
with a setup()
method: should I be using reflection and a setup method here to recreate the col
and other
objects? (I haven't done this because I don't know which Collection
implementation each test is running beforehand: if I have to manually write code using reflection to determine this, what's the point of Parameterized tests?)
My understanding is that the Parameterized tests call the constructor before each test, which should 'reset' my objects cleanly: why is this not the case?
In Java 8 this can be done fairly cleanly using lambda expressions and the utility class java.util.function.Supplier
. Instead of providing an instance of the parameter type, you provide a lambda which supplies a new instance each time it is evaluated. JUnit passes the lambda to the constructor for each test case, where a new instance is created with a call to get()
.
@RunWith(Parameterized.class)
public class CollectionsTest {
private Collection<String> col;
private Collection<String> other;
public CollectionsTest(Supplier<Collection<String>> c, Supplier<Collection<String>> other) {
this.col = c.get();
this.other = other.get();
}
@Parameterized.Parameters
public static java.util.Collection<Object[]> tokenStreams() {
Supplier<Collection<String>> c1 = () -> new DSLinkedList<String>();
Supplier<Collection<String>> c2 = () -> new DSLinkedList<String>();
return Arrays.asList(new Object[][] { { c1, c2 } });
}
@Test
public final void isEmpty() {
assertTrue(col.getClass().getName() + ".isEmpty() should return true when collection contains no elements", col.isEmpty());
col.add("Stringthing");
assertFalse(col.getClass().getName() + ".isEmpty() should return false when collection contains elements", col.isEmpty());
}
@Test
public final void size() {
assertEquals(col.getClass().getName() + ".size() should be 0 for an empty collection.", 0, col.size());
col.add("String");
assertEquals(col.getClass().getName() + ".size() should be 1 for a collection with one element.", 1, col.size());
}
Parameterized creates a new CollectionsTest object before each tests and calls the constructor, but it passes the same DSLinkedList objects each time, tokenStreams() is called only once for whole testcase. You should clean the lists in the constructor yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With