问题
I have built my own implementation of a stack in Java, which looks something like this:
There is the interface "Stack" which provides the basic functions (pop, push, peek etc.). And then I have 2 concrete classes, one with the help of arrays and the one with a linked list (how is not important in this case).
Now my question: I want to test this with JUnit5 and because you can't instantiate an interface, I have to test each function once for the class with the arrays and once for the class with the linked list, so the code is unnecessarily long. Is there a way that I can test all functions for the interface or something similar? Because if now a third implementation was added, I'd have to rewrite it all again.
I have already tried 'ParameterizedTests', but I have not made any progress.
I would be happy about help!
回答1:
I do not know what problem with @ParameterizedTest
you are facing, but as you requested this is a very generic test example which could be useful for your test:
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
...
public static Stream<Arguments> provideStacks() {
return Stream.of(
Arguments.of(new ArrayStack()),
Arguments.of(new LinkedStack())
);
}
@ParameterizedTest
@MethodSource("provideStacks")
public void test(Stack stack) {
stack.push(1);
assertEquals(1, stack.pop());
}
public interface Stack {
void push(int i);
int pop();
}
public static final class ArrayStack implements Stack {
@Override
public void push(int i) {
}
@Override
public int pop() {
return 1;
}
}
public static final class LinkedStack implements Stack {
@Override
public void push(int i) {
}
@Override
public int pop() {
return 1;
}
}
回答2:
Test Interfaces would be another possibility. You'd define your tests as default methods of an interface and implement the interface once per Stack implementation. Each implementation can add additional tests etc.
interface StackContractTests {
Stack newEmptyStack();
@Test
default void popsWhatWasLastPushed() {
Stack stack = newEmptyStack();
stack.push("foo");
assertEquals("foo", stack.pop());
}
@Test
default void cannotPopFromEmptyStack() {
Stack stack = newEmptyStack();
assertThrows(EmptyStackException.class, stack::pop);
}
}
public class ArrayListBasedStackTests implements StackContractTests {
@Override
public Stack newEmptyStack() {
return new ArrayListBasedStack();
}
}
public class LinkedListBasedStackTests implements StackContractTests {
@Override
public Stack newEmptyStack() {
return new LinkedListBasedStack();
}
}
回答3:
Make a private method which performs the test given the interface type as a parameter
private void testStack(Stack stack) {...}
Then call it in a unit test:
@Test
public void testImplementations() {
testStack(new ListStack());
testStack(new LinkedListStack());
}
来源:https://stackoverflow.com/questions/47474695/junit5-test-multiple-classes-without-repeating-code