Junit5测试框架学习总结

本文深入介绍了JUnit5的使用,包括测试注解如@Test、@BeforeEach、@AfterEach等,测试方法的执行顺序,重复测试@RepeatedTest,参数化测试@ParameterizedTest,动态测试@TestFactory,以及断言如assertAll、assertThrows和超时测试@Timeout的用法。此外,还讲解了如何禁用测试和假言测试的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 Junit5

Junit5由下面几个子模块组成:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Junit5需要在Java8或者更高版本运行。一般springboot项目已经包含了Junit的依赖。

2 各种测试使用

待测试的demo-1:

@Service
public class Calculator {
    @Autowired
    private BeanOne beanOne;

    public int add(int a, int b) {
        return a+b;
    }

    public void exception() throws Exception {
        throw new Exception();
    }

    public int getOne() {
        return beanOne.get();
    }

}

2.1 注释

Juit5常用的注解如下表(详细内容参考官网:Junit5):

常用注释
注解描述
@Test表示是测试方法。与 JUnit 4 的注释不同,此注释不声明任何属性。
@RepeatedTest表示方法是重复测试的测试模板,一般需要传入value值,表示重复次数。
@BeforeEach表示方法应该在每个@Test方法之前执行,常用于实例化对象属性。
@AfterEach表示方法应该在每个@Test方法之后执行,常用于实例化对象的一些清理工作。
@BeforeAll表示该方法在所有方法之前执行一次,一般用于初始化静态属性资源(如数据库连接),修饰static方法。
@AfterAll表示该方法在所有方法之后执行一次,用于静态资源的清理和释放。修饰static方法。
@Disabled表示该方法或者测试类将被禁用。
@Timeout表示该方法执行超时之后,将会失败。

上表的@BeforeAll@AfterAll@BeforeEach@AfterEach属于测试中的生命周期函数。

2.2 测试类和测试方法

测试类和测试方法不要使用private或public修饰符,最好省略。

一个标准的测试类如下demo-2:

public class StandardTests {
    @BeforeAll
    static void initAll() {
    }

    @BeforeEach
    void init() {
    }

    @Test
    void succeedingTest() {
    }

    @Test
    void failingTest() {
        Fail.fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @Test
    void abortedTest() {
        Assertions.assertTrue("abc".contains("Z"));
        Fail.fail("test should have been aborted");
    }

    @AfterEach
    void tearDown() {
    }

    @AfterAll
    static void tearDownAll() {
    }
}

2.3 断言

Junit5继承了Junit4的许多assertion方法,同时增加了少量用于和Java8 lambda表达式配合的断言。

所有断言位于:org.junit.jupiter.api.Assertions。

如assertAll,断言一组lambda表达式:

//源码定义
public static void assertAll(String heading, Executable... executables) throws MultipleFailuresError {
		AssertAll.assertAll(heading, executables);
	}
	
 @Test
    void groupedAssertions() {
        // In a grouped assertion all assertions are executed, and all
        // failures will be reported together.
        Assertions.assertAll("person",
                () -> Assertions.assertEquals("Jane", person.getFirstName()),
                () -> Assertions.assertEquals("Doe", person.getLastName())
        );
    }

assertAll可以进行依赖断言,也就是在assertAll可以嵌套其它断言。

异常断言assertThrows:

@Test
    public void testCalculatorException() {
        Assertions.assertThrows(Exception.class, () -> {
            calculator.exception();
        });
    }

传入异常类类型和一个函数式方法,方法中放入你测试的抛出异常的待测试方法。
Assertions.assertThrows(Class<T> expectedType, Executable executable)

超时断言assertTimeout:

@Test
    void timeoutNotExceeded() {
        // The following assertion succeeds.
        Assertions.assertTimeout(Duration.ofSeconds(5L), () -> {
            Thread.sleep(10000L);
        }) ;
    }

源码是通过判断执行任务前后的时间,决定是否测试成功,会捕获异常。
该断言如果成功会返回任务的执行结果,可以根据该结果进行下一步的判断。

assertTimeoutPreemptively超时断言:

@Test
    void timeoutExceededWithPreemptiveTermination() {
        // The following assertion fails with an error message similar to:
        // execution timed out after 10 ms
        Assertions.assertTimeoutPreemptively(Duration.ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            new CountDownLatch(1).await();
        });
    }

该断言利用线程池和具有生命周期的Future来实现任务超时判断,通过Future.get方法等待指定的timeout时间,然后根据该返回结果判断是否任务正常结束还是抛出异常or超时,从而判断是否测试成功。

与assertTimeout区别:assertTimeoutPreemptively超时会停止测试方法,并返回结果,而assertTimeout会等待任务执行结束再进行处理,也就是说如果任务有死循环,测试也会死循环。

2.4 假言测试

Junit Assumption一般位于包:org.junit.jupiter.api.Assumptions;

assumeTrue测试:

@Test
    void testOnlyOnAMDServer() {
        Assumptions.assumeTrue("AMD64".equals(System.getenv().get("PROCESSOR_ARCHITECTURE")));
        // remainder of test
    }

判断条件为true测试成功。

assumingTrue重载测试:

@Test
    void testAssumptionTrue() {
        Assumptions.assumeTrue("AMD64".equals(System.getenv().get("PROCESSOR_ARCHITECTURE")), "Aborting test if not AMD64");
    }

测试失败会抛出信息:"Aborting test if not AMD64"

assumingThat测试:

@Test
    void testThatOnlyAMD() {
        Assumptions.assumingThat("AMD64".equals(System.getenv().get("PROCESSOR_ARCHITECTURE")), () -> {
            Assertions.assertEquals(3, calculator.add(1, 2));
        });
    }

执行提供的lambda,但前提是提供的假设有效。

2.5 禁用测试

可以通过提供的@Disabled来禁用测试类或测试方法。

禁用测试类:

@Disabled("Disable test class until bug fixed")
 class DisabledClassDemo {

    @Test
    void  skipTest() {

    }
}

禁用测试方法:

@Disabled("disable test function until bug fixed")
    @Test
    void  skipTest() {

    }


    @Test
    void normalTest() {

    }

上述skipTest将会被跳过。

建议:JUnit团队建议开发人员提供一个简短的解释,说明为什么一个测试类或测试方法被禁用了。

2.6 测试顺序

Junit提供了方法测试执行顺序和测试类执行顺序(参考官网:Junit5.Test Execution Order),这里以方法测试为例:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderedTestDemo {

    @Test
    @Order(1)
    void nullValues() {
        // perform assertions against null values
    }

    @Test
    @Order(2)
    void emptyValues() {
        // perform assertions against empty values
    }

    @Test
    @Order(3)
    void validValues() {
        // perform assertions against valid values
    }
}

测试执行结果:
在这里插入图片描述
通过注解@TestMethodOrder(MethodOrderer.OrderAnnotation.class) 来说明测试类按照顺序进行测试,@Order进行测试排序,需要传入参数value。

2.7 重复测试

注释@RepeatedTest()进行重复测试,需要传入重复次数,示例demo:

@RepeatedTest(5)
    void repeatedTest() {
        Assertions.assertEquals(4, calculator.add(1, 3));
    }

执行结果:
在这里插入图片描述

查看注解源码,该注解支持自定义name属性。

2.8 参数化类型测试

Junit支持通过@ParameterizedTest和@ValueSource进行指定参数列表测试。支持的参数类型如下:

  • short
  • byte
  • int
  • long
  • float
  • double
  • char
  • boolean
  • java.lang.String
  • java.lang.Class

测试demo如下:

@ParameterizedTest
    @ValueSource(ints = { 1, 2, 3 })
    void testWithValueSource(int argument) {
        Assertions.assertTrue(argument > 0 && argument < 4);
    }

执行结果:
在这里插入图片描述

对传入的参数数组进行逐一判断。

2.9 动态测试

这种新类型的测试是一个动态测试,它是在运行时由带有@TestFactory注释的工厂方法生成的。

动态测试demo:

// error-This will result in a JUnitException!
    @TestFactory
    List<String> dynamicTestsWithInvalidReturnType() {
        return Arrays.asList("Hello");
    }
    
    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
                DynamicTest.dynamicTest("1st dynamic test", () -> Assertions.assertEquals(5, calculator.add(1, 4))),
                DynamicTest.dynamicTest("2nd dynamic test", () -> Assertions.assertEquals(4, calculator.add(2, 2)))
        );
    }

上述中dynamicTestsWithInvalidReturnType将会返回错误,因为不是动态测试支持的返回类型,通常我们有以下几种支持的返回类型:

  • Collection<DynamicTest>。
  • Iterable<DynamicTest>。
  • Iterator<DynamicTest>。
  • DynamicTest[]。
  • Stream<DynamicTest>。

上述的demo其实不算真正的动态测试,因为相当于把静态测试封装起来动态生成,每次执行结果是一样的。下述demo是真实动态的测试:

@TestFactory
    Stream<DynamicTest> generateRandomNumberOfTestsFromIterator() {

        // Generates random positive integers between 0 and 100 until
        // a number evenly divisible by 7 is encountered.
        Iterator<Integer> inputGenerator = new Iterator<Integer>() {

            Random random = new Random();
            int current;

            @Override
            public boolean hasNext() {
                current = random.nextInt(100);
                return current % 3 == 0;
            }

            @Override
            public Integer next() {
                return current;
            }
        };

        // Generates display names like: input:5, input:37, input:85, etc.
        Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

        // Executes tests based on the current input value.
        ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 3 != 0);

        // Returns a stream of dynamic tests.
        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
    }

这个demo测试是动态生成的,而且流的大小每次会不同,因为hasNext每次不同,这个测试更符合我们动态测试的概念。

我们可以利用inputGenerator生成我们测试的测试参数,动态变化,记得hasNext设置随机跳出条件。
然后利用testExecutor设置我们的测试条件。

2.10 超时测试

注释@Timeout运行终止一个测试,当达到超时时间设置。

测试demo如下:

@Test
    @Timeout(value = 5, unit = TimeUnit.SECONDS)
    void testTimeout() {
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果:
在这里插入图片描述
这里可以看出,@Timeout只是在任务结束后,判断执行时间是否测试成功,不会主动停止任务。

参考文献

[1] Junit5官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LamaxiyaFc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值