SpringBoot中使用单元测试:Junit5

本文详细介绍了JUnit5的使用,包括@Test、@BeforeEach、@AfterEach等核心注解,以及断言机制、异常断言、超时断言和参数化测试的使用方法。同时,对比了JUnit4与JUnit5的主要区别,帮助开发者更好地理解和应用JUnit5进行单元测试。

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

注意:Junit5只有在SpringBoot2.2以上才可以使用
点击进入官网

pom.xml

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

常用注解

  • @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
  • @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
  • @DisplayName :为测试类或者测试方法设置展示名称
  • @BeforeEach :表示在每个单元测试之前执行
  • @AfterEach :表示在每个单元测试之后执行
  • @BeforeAll :表示在所有单元测试之前执行//该注解必须注在静态方法上
  • @AfterAll :表示在所有单元测试之后执行//该注解必须注在静态方法上
  • @Tag :表示单元测试类别,类似于JUnit4中的@Categories,给测试方法打一个标签
  • @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
  • @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
  • @RepeatedTest(int n) :表示测试方法要执行n次,这个注解不要和@Test一块用
@Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
@SpringBootTest
public class JunitTest {

    @Autowired
    User user;

    @BeforeAll
    static void testBeforeAll(){
        System.out.println("所有测试方法的开始");
    }

    @AfterAll
    static void testAfterAll(){
        System.out.println("所有测试方法的结束");
    }

    @BeforeEach
    void testBeforeEach(){
        System.out.println("一个测试就要开始了");
    }

    @AfterEach
    void testAfterEach(){
        System.out.println("一个测试已经结束了");
    }

    @Test
    @Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
    @DisplayName("测试Display方法")
    @Tag
    public void test(){
        System.out.println("测试junit5");
        System.out.println(user);
    }

    @Disabled
    @Test
    @DisplayName("测试Display方法")
    public void test2(){
        System.out.println("测试junit5-2");
    }
}

断言机制(assertions)

  • 断言简介
    断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法
  • 断言作用
    检查业务逻辑返回的数据是否合理。
    所有的测试运行结束以后,会有一个详细的测试报告;
  • 断言的机制
    我们通过调用Assertions的静态方法,并给它传入相应的参数,断言方法会在判断这传入的参数符合不符合条件,如果不符合条件他就会抛出一个错误
    JUnit 5 内置的断言可以分成如下几个类别:

简单断言

对单个值进行验证
在这里插入图片描述

    @Test
    @DisplayName("测试简单断言")
    public void test(){
        try {
            assertEquals(2, 1,"怎么能是不相等的?");
        } catch (Exception e) {
            e.printStackTrace();
        }
//        System.out.println("断言成功");
        assertSame(127, 127);
        assertNotSame(128, 128);
        assertTrue(1 == 2,"居然是假的!");
        assertFalse(1 > 1);
        assertNull(null);
        assertNotNull(new String());
    }

数组断言

验证数组的里的值是否完全相同,只有一个方法 assertArrayEquals(数组,数组,信息字符串)

@Test
    @DisplayName("测试数组断言")
    void testArrayAssert(){
        int[] array = {1,2};
        int[] array2 = {2,1};
        assertArrayEquals(array,array2,"数组数据不相同");
    }

组合断言

可以组合多个断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言

@Test
@DisplayName("assert all")
public void all() {
 assertAll("Math",
    () -> assertEquals(2, 1 + 1),
    () -> assertTrue(1 > 0)
 );
}

异常断言

顾名思义,我断言下面的代码会出异常,如果没出异常,那我们就抛出error
方法:assertThrows

@Test
@DisplayName("测试异常断言")
void testExceptionAssert(){
    assertThrows(Exception.class, ()->{int i = 1/0;},"竟然没有出异常?");
    System.out.println("断言成功");
}

超时断言

顾名思义:我断言下面的代码会在多少时间内完成,不完成就抛出error
指定时间使用Durationofxxx方法

    @Test
    @DisplayName("测试超时断言")
    void testTimeoutAssert(){
        assertTimeout(Duration.ofMillis(1000),()->Thread.sleep(999));
    }

快速失败

通过 fail 方法直接使得测试失败

    @Test
    @DisplayName("测试快速失败")
    void testFail(){
        fail("我失败了,我不能接受");
    }

小知识

写完测试类之后我们可以使用测试组件进行测试,会对所有单元测试进行测试,并且告诉我们出了多少错

//输出的信息
[INFO] 
[ERROR] Tests run: 14, Failures: 3, Errors: 1, Skipped: 1
Tests run: 14, Failures: 3, Errors: 1, Skipped: 1

在这里插入图片描述

前置条件

JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

    @Test
    @DisplayName("测试前置条件")
    void testAssumptions(){
        Assumptions.assumeTrue(true,"他不是正确的!555");
        System.out.println("111");
    }

在我看来,前置条件类似于给@Disable添加了一个条件
当错误的时候该测试就不往下执行了,就不测试了

嵌套测试

通过@Nested注解来实现,写在内部类上,外部类的测试方法不会调用内部类的BeforeEach方法,内部类里面的测试方法会调用外部类的BeforeEach方法

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, stack::peek);
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}

参数化测试

参数化测试可以使用不同的参数多次运行测试。它们的声明就像常规@Test方法一样,但使用 @ParameterizedTest注释代替。此外,您必须至少声明一个 源,该源将为每次调用提供参数,然后在测试方法中使用这些参数。

  • @ValueSource() 也就是和foreach差不多,把数组中的数据遍历一遍进行测试。
    @ParameterizedTest
    @ValueSource(ints = {1,2,3})
    void test(int i){
        System.out.println(i);
    }
  • @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
  • @NullSource: 表示为参数化测试提供一个null的入参
  • @EnumSource: 表示为参数化测试提供一个枚举入参
  • @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
  • @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

Junit4与5的差别

在进行迁移的时候需要注意如下的变化:

  • 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
  • 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
  • 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
  • 把@Ignore 替换成@Disabled。
  • 把@Category 替换成@Tag。
  • 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

狴犴ys

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

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

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

打赏作者

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

抵扣说明:

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

余额充值