JUnit5: Test multiple classes without repeating co

2019-08-07 14:26发布

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!

标签: java junit5
3条回答
趁早两清
2楼-- · 2019-08-07 14:29

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();
    }
}
查看更多
你好瞎i
3楼-- · 2019-08-07 14:37

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());
}
查看更多
The star\"
4楼-- · 2019-08-07 14:54

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;
  }
}
查看更多
登录 后发表回答