测试接口和默认方法

JUnit Jupiter允许在接口default方法中声明@Test@RepeatedTest@ParameterizedTest@ TestFactory@TestTemplate@BeforeEach@AfterEach。如果测试接口或测试类用@TestInstance(Lifecycle.PER_CLASS)注解(请参阅测试实例生命周期),则可以在测试接口中的static方法或接口default方法上声明@BeforeAll@AfterAll。这里有些例子。

  1. @TestInstance(Lifecycle.PER_CLASS)
  2. interface TestLifecycleLogger {
  3. static final Logger LOG = Logger.getLogger(TestLifecycleLogger.class.getName());
  4. @BeforeAll
  5. default void beforeAllTests() {
  6. LOG.info("Before all tests");
  7. }
  8. @AfterAll
  9. default void afterAllTests() {
  10. LOG.info("After all tests");
  11. }
  12. @BeforeEach
  13. default void beforeEachTest(TestInfo testInfo) {
  14. LOG.info(() -> String.format("About to execute [%s]",
  15. testInfo.getDisplayName()));
  16. }
  17. @AfterEach
  18. default void afterEachTest(TestInfo testInfo) {
  19. LOG.info(() -> String.format("Finished executing [%s]",
  20. testInfo.getDisplayName()));
  21. }
  22. }
  1. interface TestInterfaceDynamicTestsDemo {
  2. @TestFactory
  3. default Collection<DynamicTest> dynamicTestsFromCollection() {
  4. return Arrays.asList(
  5. dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
  6. dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
  7. );
  8. }
  9. }

可以在测试接口上声明@ExtendWith@Tag,以便实现该接口的类自动继承其注解和扩展。请参阅测试执行前后的回调来获取TimingExtension的源代码。

  1. @Tag("timed")
  2. @ExtendWith(TimingExtension.class)
  3. interface TimeExecutionLogger {
  4. }

在你的测试类中,你可以实现这些测试接口来应用它们。

  1. class TestInterfaceDemo implements TestLifecycleLogger,
  2. TimeExecutionLogger, TestInterfaceDynamicTestsDemo {
  3. @Test
  4. void isEqualValue() {
  5. assertEquals(1, 1, "is always equal");
  6. }
  7. }

运行TestInterfaceDemo,输出类似以下内容:

  1. :junitPlatformTest
  2. INFO example.TestLifecycleLogger - Before all tests
  3. INFO example.TestLifecycleLogger - About to execute [dynamicTestsFromCollection()]
  4. INFO example.TimingExtension - Method [dynamicTestsFromCollection] took 13 ms.
  5. INFO example.TestLifecycleLogger - Finished executing [dynamicTestsFromCollection()]
  6. INFO example.TestLifecycleLogger - About to execute [isEqualValue()]
  7. INFO example.TimingExtension - Method [isEqualValue] took 1 ms.
  8. INFO example.TestLifecycleLogger - Finished executing [isEqualValue()]
  9. INFO example.TestLifecycleLogger - After all tests
  10. Test run finished after 190 ms
  11. [ 3 containers found ]
  12. [ 0 containers skipped ]
  13. [ 3 containers started ]
  14. [ 0 containers aborted ]
  15. [ 3 containers successful ]
  16. [ 0 containers failed ]
  17. [ 3 tests found ]
  18. [ 0 tests skipped ]
  19. [ 3 tests started ]
  20. [ 0 tests aborted ]
  21. [ 3 tests successful ]
  22. [ 0 tests failed ]
  23. BUILD SUCCESSFUL

这个特性的另一个可以应用的地方是为接口契约编写测试。例如,您可以编写测试,验证Object.equalsComparable.compareTo的实现应该如何如下表现。

  1. public interface Testable<T> {
  2. T createValue();
  3. }
  1. public interface EqualsContract<T> extends Testable<T> {
  2. T createNotEqualValue();
  3. @Test
  4. default void valueEqualsItself() {
  5. T value = createValue();
  6. assertEquals(value, value);
  7. }
  8. @Test
  9. default void valueDoesNotEqualNull() {
  10. T value = createValue();
  11. assertFalse(value.equals(null));
  12. }
  13. @Test
  14. default void valueDoesNotEqualDifferentValue() {
  15. T value = createValue();
  16. T differentValue = createNotEqualValue();
  17. assertNotEquals(value, differentValue);
  18. assertNotEquals(differentValue, value);
  19. }
  20. }
  1. public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {
  2. T createSmallerValue();
  3. @Test
  4. default void returnsZeroWhenComparedToItself() {
  5. T value = createValue();
  6. assertEquals(0, value.compareTo(value));
  7. }
  8. @Test
  9. default void returnsPositiveNumberComparedToSmallerValue() {
  10. T value = createValue();
  11. T smallerValue = createSmallerValue();
  12. assertTrue(value.compareTo(smallerValue) > 0);
  13. }
  14. @Test
  15. default void returnsNegativeNumberComparedToSmallerValue() {
  16. T value = createValue();
  17. T smallerValue = createSmallerValue();
  18. assertTrue(smallerValue.compareTo(value) < 0);
  19. }
  20. }

在你的测试类中,你可以实现两个契约接口,从而继承相应的测试。当然你必须实现抽象方法。

  1. class StringTests implements ComparableContract<String>, EqualsContract<String> {
  2. @Override
  3. public String createValue() {
  4. return "foo";
  5. }
  6. @Override
  7. public String createSmallerValue() {
  8. return "bar"; // 'b' < 'f' in "foo"
  9. }
  10. @Override
  11. public String createNotEqualValue() {
  12. return "baz";
  13. }
  14. }

上述测试仅仅是作为例子,因此不完整。