JUnit5
安卓build.gradle
https://github.com/mannodermaus/android-junit5
Unit 3 或 JUnit4 的向后兼容性
注解
- 所有核心注解位于
junit-jupiter-api
模块中的org.junit.jupiter.api
包中。
- 测试类和测试方法都不必是
public
。
注解 |
描述 |
@Test |
表示方法是测试方法。与JUnit4的@Test注解不同的是,这个注解没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于他们自己的专用注解来操作的。除非被覆盖,否则这些方法可以继承。 |
@ParameterizedTest |
表示方法是参数化测试。 除非被覆盖,否则这些方法可以继承。 |
@RepeatedTest |
表示方法是用于重复测试的测试模板。除非被覆盖,否则这些方法可以继承。 |
@TestFactory |
表示方法是用于动态测试的测试工厂。除非被覆盖,否则这些方法可以继承。 |
@TestInstance |
用于为被注解的测试类配置测试实例生命周期。 这个注解可以继承。 |
@TestTemplate |
表示方法是测试用例的模板,设计为被调用多次,调用次数取决于自注册的提供者返回的调用上下文。除非被覆盖,否则这些方法可以继承。 |
@DisplayName |
声明测试类或测试方法的自定义显示名称。这个注解不被继承。 |
@BeforeEach |
表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之前执行; 类似于JUnit 4的@Before。 除非被覆盖,否则这些方法可以继承。 |
@AfterEach |
表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之后执行; 类似于JUnit 4的@After。 除非被覆盖,否则这些方法可以继承。 |
@BeforeAll |
表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之前执行; 类似于JUnit 4的@BeforeClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。 |
@AfterAll |
表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之后执行; 类似于JUnit 4的@AfterClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。 |
@Nested |
表示被注解的类是一个嵌套的非静态测试类。除非使用“per-class”测试实例生命周期,否则@BeforeAll和@AfterAll方法不能直接在@Nested测试类中使用。 这个注解不能继承。 |
@Tag |
在类或方法级别声明标签,用于过滤测试; 类似于TestNG中的test group或JUnit 4中的Categories。这个注释可以在类级别上继承,但不能在方法级别上继承。 |
@Disabled |
用于禁用测试类或测试方法; 类似于JUnit4的@Ignore。这个注解不能继承。 |
@ExtendWith |
用于注册自定义扩展。 这个注解可以继承。 |
| import static org.junit.jupiter.api.Assertions.*; |
| |
| import org.junit.jupiter.api.*; |
| |
| class FirstJUnit5Tests { |
| |
| |
| |
| |
| |
| @BeforeAll |
| static void beforeAll(){ |
| System.out.println("before all"); |
| } |
| |
| @AfterAll |
| static void afterAll(){ |
| System.out.println("after all"); |
| } |
| |
| |
| |
| |
| @BeforeEach |
| void beforeEach(){ |
| System.out.println("before each"); |
| } |
| |
| @AfterEach |
| void afterEach(){ |
| System.out.println("after each"); |
| } |
| |
| @DisplayName("测试一") |
| @Test |
| void test1() { |
| assertEquals(2, 2, "error"); |
| } |
| |
| |
| |
| |
| @Disabled |
| @Test |
| void test2(){ |
| System.out.println(2); |
| } |
| } |
| import static org.junit.jupiter.api.Assertions.*; |
| |
| import org.junit.jupiter.api.*; |
| |
| class FirstJUnit5Tests { |
| @BeforeEach |
| void beforeEach(RepetitionInfo info){ |
| int currentRepetition = info.getCurrentRepetition(); |
| int totalRepetition = info.getTotalRepetitions(); |
| System.out.println("before each " + currentRepetition + "/" + totalRepetition); |
| } |
| |
| @AfterEach |
| void afterEach(RepetitionInfo info){ |
| int currentRepetition = info.getCurrentRepetition(); |
| int totalRepetition = info.getTotalRepetitions(); |
| System.out.println("after each " + currentRepetition + "/" + totalRepetition); |
| } |
| |
| @DisplayName("测试三") |
| |
| |
| @RepeatedTest(value = 3) |
| void test3(){ |
| System.out.println("test3 executed"); |
| } |
| } |

| @Tag("tagA") |
| @Test |
| void test1(){ |
| System.out.println("test1"); |
| } |
| |
| @Tag("tagA") |
| @Tag("tagC") |
| @Test |
| void test2(){ |
| System.out.println("test2"); |
| } |
| |
| @Tag("tagB") |
| @Tag("tagC") |
| @DisplayName("测试三") |
| @RepeatedTest(value = 3) |
| void test3(){ |
| System.out.println("test3 executed"); |
| } |
断言
| Assertions.assertEquals() and Assertions.assertNotEquals() |
| Assertions.assertArrayEquals() |
| Assertions.assertIterableEquals() |
| Assertions.assertLinesMatch() |
| Assertions.assertNotNull() and Assertions.assertNull() |
| Assertions.assertNotSame() and Assertions.assertSame() |
| Assertions.assertTimeout() and Assertions.assertTimeoutPreemptively() |
| Assertions.assertTrue() and Assertions.assertFalse() |
| Assertions.assertThrows() |
| Assertions.fail() |
Assertions.assertEquals()
| |
| |
| |
| |
| @Test |
| void test1() { |
| System.out.println("test1"); |
| |
| Assertions.assertEquals(4, 4); |
| |
| |
| Assertions.assertEquals(3, 4, "Calculator.add(2, 2) test failed"); |
| |
| |
| Supplier<String> messageSupplier = () -> "Calculator.add(2, 2) test failed"; |
| Assertions.assertEquals(3, 4, messageSupplier); |
| } |
| |
| |
| |
| |
| |
| |
| |
| @Test |
| void test2() { |
| Assertions.assertNotEquals(3, 4); |
| } |
| static void assertEquals(Object expected, Object actual, String message) { |
| if (!AssertionUtils.objectsAreEqual(expected, actual)) { |
| AssertionUtils.failNotEqual(expected, actual, message); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static boolean objectsAreEqual(Object obj1, Object obj2) { |
| if (obj1 == null) { |
| return obj2 == null; |
| } else { |
| return obj1.equals(obj2); |
| } |
| } |
Assertions.assertArrayEquals()
| @Test |
| void test1() { |
| |
| Assertions.assertArrayEquals(new int[]{1,2,3}, new int[]{1,2,3}, "Array Equal Test"); |
| |
| |
| Assertions.assertArrayEquals(new int[]{1,2,3}, new int[]{1,3,2}, "Array Equal Test"); |
| |
| |
| Assertions.assertArrayEquals(new int[]{1,2,3}, new int[]{1,2,3,4}, "Array Equal Test"); |
| } |
Assertions.assertIterableEquals()
| |
| |
| |
| |
| @Test |
| void test1() { |
| Iterable<Integer> listOne = new ArrayList<>(Arrays.asList(1,2,3,4)); |
| Iterable<Integer> listTwo = new ArrayList<>(Arrays.asList(1,2,3,4)); |
| Iterable<Integer> listThree = new ArrayList<>(Arrays.asList(1,2,3)); |
| Iterable<Integer> listFour = new ArrayList<>(Arrays.asList(1,2,4,3)); |
| |
| |
| Assertions.assertIterableEquals(listOne, listTwo); |
| |
| |
| Assertions.assertIterableEquals(listOne, listThree); |
| |
| |
| Assertions.assertIterableEquals(listOne, listFour); |
| } |
Assertions.assertLinesMatch()
它断言期望的字符串列表与实际列表相匹配。 将一个字符串与另一个字符串匹配的逻辑是:
- 检查
expected.equals(actual)
–如果是,则继续下一对
- 否则将
expected
视为正则表达式,并通过
String.matches(String)
检查–如果是,则继续下一对
- 否则检查
expected
行是否为快进标记,如果是,则相应地应用
快速前行并转到 1。
有效的快进标记是以>>
开头和结尾并且至少包含 4 个字符的字符串。 快进文字之间的任何字符都将被丢弃。
| >>>> |
| >> stacktrace >> |
| >> single line, non Integer.parse()-able comment >> |
Assertions.assertNotNull()
| @Test |
| void test1() { |
| String nullString = null; |
| String notNullString = "haha"; |
| |
| |
| Assertions.assertNotNull(notNullString); |
| |
| |
| Assertions.assertNotNull(nullString); |
| |
| |
| Assertions.assertNull(nullString); |
| |
| |
| Assertions.assertNull(notNullString); |
| } |
Assertions.assertNotSame()
assertNotSame()
断言预期和实际不引用同一对象。 同样,assertSame()
方法断言,预期和实际引用完全相同的对象
| @Test |
| void test1() { |
| |
| String originalObject = "haha"; |
| String cloneObject = originalObject; |
| String otherObject = "haha"; |
| |
| |
| String otherObject2 = new String(new char[]{'h', 'a','h', 'a'}); |
| |
| |
| Assertions.assertNotSame(originalObject, "hehe"); |
| |
| |
| Assertions.assertNotSame(originalObject, cloneObject); |
| |
| |
| Assertions.assertSame(originalObject, cloneObject); |
| |
| |
| Assertions.assertSame(originalObject, "hehe"); |
| } |
String为什么不用new:https://www.debugease.com/j2se/3709966.html
Assertions.assertTimeout()
| |
| |
| |
| |
| @Test |
| void test1() { |
| |
| Assertions.assertTimeout(Duration.ofMinutes(1), () -> "result"); |
| |
| |
| Assertions.assertTimeout(Duration.ofMillis(100), () -> { |
| Thread.sleep(200); |
| return "result"; |
| }); |
| |
| |
| Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> { |
| Thread.sleep(200); |
| return "result"; |
| }); |
| } |
Assertions.assertTrue()
| @Test |
| void testCase() { |
| |
| boolean trueBool = true; |
| boolean falseBool = false; |
| |
| Assertions.assertTrue(trueBool); |
| Assertions.assertTrue(falseBool, "test execution message"); |
| Assertions.assertTrue(falseBool, FirstJUnit5Tests::message); |
| Assertions.assertTrue(FirstJUnit5Tests::getResult, FirstJUnit5Tests::message); |
| } |
| |
| private static String message() { |
| return "Test execution result"; |
| } |
| |
| private static boolean getResult() { |
| return false; |
| } |
Assertions.assertThrows()
| |
| |
| |
| @Test |
| void testExpectedException() { |
| Assertions.assertThrows(NumberFormatException.class, () -> { |
| Integer.parseInt("One"); |
| }); |
| } |
| |
| |
| |
| |
| @Test |
| void testExpectedExceptionWithSuperType() { |
| Assertions.assertThrows(IllegalArgumentException.class, () -> { |
| Integer.parseInt("One"); |
| }); |
| } |
| |
| |
| |
| |
| @Test |
| void testExpectedExceptionFail() { |
| Assertions.assertThrows(IllegalArgumentException.class, () -> { |
| Integer.parseInt("1"); |
| }); |
| } |
Assertions.fail()
| public static void fail(String message) |
| public static void fail(Throwable cause) |
| public static void fail(String message, Throwable cause) |
| public static void fail(Supplier<String> messageSupplier) |
假设
Assumptions.assumeTrue()
| @Test |
| void testOnDev() { |
| Assumptions.assumeTrue(true); |
| |
| System.out.println("haha"); |
| } |
| |
| @Test |
| void testOnProd() { |
| Assumptions.assumeTrue(false, FirstJUnit5Tests::message); |
| |
| System.out.println("xixi"); |
| } |
| |
| private static String message() { |
| return "TEST Execution Failed :: "; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步