junit 5入門シリーズチュートリアル-13-junit 5テストインタフェースおよびデフォルト方法


目次

  • ディレクトリ
  • テストインタフェースとデフォルトメソッド
  • 定義インタフェース
  • テストケース
  • インタフェース契約
  • インタフェース定義
  • テストクラス
  • シリーズナビゲーション
  • テストインタフェースとデフォルトメソッド


    JUnit Jupiterでは、インタフェースのデフォルトメソッドで@Test、@RepeatedTest、@ParameterizedTest、@TestFactory、@TestTemplate、@BeforeEach、@AfterEachを宣言できます.テストインタフェースまたはテストクラスが@TestInstance(Lifecycle.PER_CLASS)によって注釈されている場合、@BeforeAllおよび@AfterAllは、テストインタフェースの静的メソッドで宣言してもよいし、インタフェースのデフォルトメソッドで宣言してもよい.
    ここにはいくつかの例があります.

    インタフェースの定義

  • TestLifecycleLogger.java
  • import org.junit.jupiter.api.AfterAll;
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.TestInfo;
    import org.junit.jupiter.api.TestInstance;
    
    import java.util.logging.Logger;
    
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    public interface TestLifecycleLogger {
    
        static final Logger LOG = Logger.getLogger(TestLifecycleLogger.class.getName());
    
        @BeforeAll
        default void beforeAllTests() {
            LOG.info("Before all tests");
        }
    
        @AfterAll
        default void afterAllTests() {
            LOG.info("After all tests");
        }
    
        @BeforeEach
        default void beforeEachTest(TestInfo testInfo) {
            LOG.info(() -> String.format("About to execute [%s]",
                    testInfo.getDisplayName()));
        }
    
        @AfterEach
        default void afterEachTest(TestInfo testInfo) {
            LOG.info(() -> String.format("Finished executing [%s]",
                    testInfo.getDisplayName()));
        }
    }
  • TestInterfaceDynamicTestsDemo.java
  • import org.junit.jupiter.api.DynamicTest;
    import org.junit.jupiter.api.TestFactory;
    
    import java.util.Arrays;
    import java.util.Collection;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    import static org.junit.jupiter.api.DynamicTest.dynamicTest;
    
    public interface TestInterfaceDynamicTestsDemo {
    
        @TestFactory
        default Collection dynamicTestsFromCollection() {
            return Arrays.asList(
                    dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
                    dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
            );
        }
    }
    @ExtendWithおよび@Tagは、そのインタフェースのクラスがタグおよび拡張を自動的に継承することを実現するために、テストインタフェース上で宣言され得る.TimingExtensionソースコードに対するテスト実行前後のコールバックを表示します.
  • TimeExecutionLogger.java
  • @Tag("timed")
    @ExtendWith(TimingExtension.class)
    interface TimeExecutionLogger {
    }
  • TimingExtension.java
  • import java.lang.reflect.Method;
    import java.util.logging.Logger;
    
    import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
    import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
    import org.junit.jupiter.api.extension.ExtensionContext;
    import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
    import org.junit.jupiter.api.extension.ExtensionContext.Store;
    
    public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
    
        private static final Logger logger = Logger.getLogger(TimingExtension.class.getName());
    
        private static final String START_TIME = "start time";
    
        @Override
        public void beforeTestExecution(ExtensionContext context) throws Exception {
            getStore(context).put(START_TIME, System.currentTimeMillis());
        }
    
        @Override
        public void afterTestExecution(ExtensionContext context) throws Exception {
            Method testMethod = context.getRequiredTestMethod();
            long startTime = getStore(context).remove(START_TIME, long.class);
            long duration = System.currentTimeMillis() - startTime;
    
            logger.info(() -> String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
        }
    
        private Store getStore(ExtensionContext context) {
            return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));
        }
    }

    テストケース

  • TestInterfaceDemo.java
  • import org.junit.jupiter.api.Test;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    class TestInterfaceDemo implements TestLifecycleLogger,
            TimeExecutionLogger, TestInterfaceDynamicTestsDemo {
    
        @Test
        void isEqualValue() {
            assertEquals(1, 1, "is always equal");
        }
    }

    ログは次のとおりです.
    Jun 25, 2018 6:18:43 PM com.github.houbb.jdk.junit5.interfaces.TestLifecycleLogger beforeAllTests
     : Before all tests
    Jun 25, 2018 6:18:43 PM com.github.houbb.jdk.junit5.interfaces.TestLifecycleLogger beforeEachTest
     : About to execute [isEqualValue()]
    Jun 25, 2018 6:18:43 PM com.github.houbb.jdk.junit5.interfaces.TimingExtension afterTestExecution
     : Method [isEqualValue] took 4 ms.
    Jun 25, 2018 6:18:43 PM com.github.houbb.jdk.junit5.interfaces.TestLifecycleLogger afterEachTest
     : Finished executing [isEqualValue()]
    Jun 25, 2018 6:18:43 PM com.github.houbb.jdk.junit5.interfaces.TestLifecycleLogger afterAllTests
     : After all tests

    インタフェース契約


    この特性のもう一つの可能な応用はインタフェース契約のためにテストを記述することである.
    たとえば、オブジェクトの実装方法のテストを作成できます.Object.equalsまたはComparable.compareTo.

    インタフェース定義

  • Testable.java
  • public interface Testable<T> {
    
        T createValue();
    
    }
  • EqualsContract.java
  • import org.junit.jupiter.api.Test;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.junit.jupiter.api.Assertions.assertNotEquals;
    
    public interface EqualsContract<T> extends Testable<T> {
    
        T createNotEqualValue();
    
        @Test
        default void valueEqualsItself() {
            T value = createValue();
            assertEquals(value, value);
        }
    
        @Test
        default void valueDoesNotEqualNull() {
            T value = createValue();
            assertFalse(value.equals(null));
        }
    
        @Test
        default void valueDoesNotEqualDifferentValue() {
            T value = createValue();
            T differentValue = createNotEqualValue();
            assertNotEquals(value, differentValue);
            assertNotEquals(differentValue, value);
        }
    }
  • ComparableContract.java
  • import org.junit.jupiter.api.Test;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {
    
        T createSmallerValue();
    
        @Test
        default void returnsZeroWhenComparedToItself() {
            T value = createValue();
            assertEquals(0, value.compareTo(value));
        }
    
        @Test
        default void returnsPositiveNumberComparedToSmallerValue() {
            T value = createValue();
            T smallerValue = createSmallerValue();
            assertTrue(value.compareTo(smallerValue) > 0);
        }
    
        @Test
        default void returnsNegativeNumberComparedToSmallerValue() {
            T value = createValue();
            T smallerValue = createSmallerValue();
            assertTrue(smallerValue.compareTo(value) < 0);
        }
    }

    テストクラス


    テストクラスでは、この2つの契約インタフェースを実装し、対応するテストを継承できます.もちろん、抽象的な方法を実装する必要があります.
  • StringTest.java
  • public class StringTest
            implements ComparableContract<String>, EqualsContract<String>{
        @Override
        public String createValue() {
            return "foo";
        }
    
        @Override
        public String createSmallerValue() {
            return "bar"; // 'b' < 'f' in "foo"
        }
    
        @Override
        public String createNotEqualValue() {
            return "baz";
        }
    }

    シリーズナビゲーション


    シリーズナビゲーション