JUnit5单元测试(基于SpringBoot)

7 篇文章 0 订阅
1 篇文章 0 订阅

1.JUnit5版本的出现

相信很多小伙伴们都用过JUnit4,但是自从SpringBoot 2.2.0 版本的出现,JUnit5就作为了单元测试的默认库。

毛哥(博主)我使用的SpringBoot2.6.7,默认的JUnit版本仲裁 5.8.2
在这里插入图片描述

所以今天咱们就来聊聊最新版本JUnit5
JUnit5与之前版本(JUnit3/4)做了很大的改变。它由三个不同子项目的几个模块组成:
在这里插入图片描述

  • JUnit Platform:Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。

  • JUnit Jupiter:Junit Jupiter提供了Junit5的新的编程模型,是Junit新特性的核心。内部包含了一个测试引擎,用于在Junit Platform上运行。

  • JUnit Vintage:JUnit Vintage 提供了兼容JUnit4.x,JUnit3.x的测试引擎,可以在Junit5平台上运行JUnit3和4的测试用例。

想了解更多内容:点击去junit5官网

看完之后,相信大家对Junit5有了大概的了解!!!

2.JUnit5的使用

系统环境要求:

  • java8及以上
  • maven3.3及以上
  • idea开发工具

1、导入test的场景

  • 如果创建的是maven项目需导入单元测试test的场景:
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    
  • 如果是创建的Spring Initilizr 会自动引入单元测试test的场景,就不需要我们手动添加:
    在这里插入图片描述

2、JUnit5的常用注解

所有的核心注释基本上都位于 junit-jupiter-api 模块中的 org.junit.jupiter.api 包中。

注解说明
@Test表示该方法是一个测试方法。但是与JUnit4的注解不同是,它没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于它们自己的专用注解来完成的
@ParameterizedTest表示该方法是一个参数化测试方法
@RepeatedTest表示方法可重复执行的测试方法
@TestFactory表示该方法是一个测试工厂
@DisplayName标注测试名称,用来方便查看
@DisplayNameGeneration用来生成@DisplayName的注解,配合DisplayNameGeneration类来使用
@BeforeEach表示在当前类中每个使用@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前执行
@AfterEach表示在当前类中每个使用@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后执行
@BeforeAll表示在所有使用@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前执行
@AfterAll表示在所有使用@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后执行
@Nested表示使用了该注解的类是一个内嵌、非静态的测试类(下面会详细解释)
@Tag用于声明过滤测试的tags,该注解可以用在方法或类上,类似于TesgNG的测试或Junit4的分类
@Disabled表示禁用测试,可以禁用一个测试类或者一个测试方法,类似于Junti4的@Ignore
@Timeout表示测试方法运行如果超过了指定时间将会返回错误
@ExtendWith为测试类或测试方法提供扩展类引用,可以用于注册自定义
```java
//
import org.junit.jupiter.api.Test;//可以这里使用的的是Junit5的Test注解

@Test
@DisplayName("第一次测试")
public void firstName(){
    System.out.println("hello world");
}
```

3、JUnit5的 断言(assertions)

断言(assertions):测试方法的核心部分,用来对测试需要满足的条件验证。断言方法都是 org.junit.jupiter.api.Assertions 中的静态方法,可以使用Lambda表达式。断言的主要功能就是检查业务逻辑返回的数据是否合理

JUnit5内置的断言可以分为几个类别:

1. 简单断言

用来对单个数据进行验证,如下方法:

方法说明
assertEquals判断两个对象或两个原始类型是否相等
assertNotEquals判断两个对象或两个原始类型是否不相等
assertSame判断两个对象引用是否指向同一个对象
assertNotSame判断两个对象引用是否指向不同的对象
assertTrue判断给定的布尔值是否为 true
assertFalse判断给定的布尔值是否为 false
assertNull判断给定的对象引用是否为 null
assertNotNull判断给定的对象引用是否不为 null

示例代码:

/**
 * 注意:断言:前面断言失败,后面的代码都不会执行
 */
@Test
    @DisplayName("测试简单断言")
    void testSimpleAssertions(){
        //测试method:assertEquals
        Assertions.assertEquals(1, 1, "不相等");
        //测试method:assertNotSame
        Assertions.assertNotSame(new String("maoge"),new String("maoge") ,"两个对象不相同" );
        //测试method:assertNull
        Assertions.assertTrue(1<3, "值为false");
        //测试method:assertNotNull
        Assertions.assertNotNull(new Object(),"对象不为空");
    }

2. 数组断言

数组断言主要是通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等:

示例代码:

@Test
    @DisplayName("测试数组断言")
    void array(){
        Assertions.assertArrayEquals(new String[]{"maoge"}, new String[]{"maoge"},"数组不相同");
    }

3. 组合断言

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

示例代码:

@Test
    @DisplayName("测试组合断言")
    void assertAll(){
        /**
         * 所有断言全部需要成功方可通行
         */
        Assertions.assertAll("assert All",
                () -> assertEquals(1, 2 - 1),
                () -> assertTrue(true),
                () -> assertNotNull(new Object())
        );
    }

4. 异常断言

相比于Junit4,junit5的测试方法异常的断言方式要比junit4简单很多。Junit5提供了一种新的断言方式:Assertions.assertThrow(),然后配合函数式编程进行使用。

@DisplayName("测试异常断言")
    @Test
    void testException(){
        //断定业务逻辑一定出现异常
        assertThrows(ArithmeticException.class, ()->{int i=1/0;},"业务逻辑居然正常运行?");
        System.out.println("除数不能为0的原则");
    }

5. 超时断言

  • 以前的超时断言写法(使用@Timeout注解):

    /**
         * 规定方法的超时时间,超出时间测试出异常
         * @throws InterruptedException
         * 
         * @Timeout参数说明:
         * value:设置的超时时间 
         * unit:指定的超时参数单位
         */
        @Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
        @Test
        @DisplayName("测试超时断言")
        void testTimeout() throws InterruptedException {
            Thread.sleep(600);
        }
    
  • 有了Junit5通过 Assertions.assertTimeout() 方法为测试方法设置超时时间,如果测试方法的执行时间大于指定的超时参数,测试方法将抛出异常,测试结果为失败。

    /*
     * assertionTimeout()参数说明:
     * 参数一:Duration 设置超时时间
     * 参数二:函数式对象调用超时的方法
     */
    @Test
    @DisplayName("测试超时断言")
    public void timeoutTest() {
        //如果测试方法时间超过1s将会异常
        Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
    }
    

6. 快速失败

通过fail方法直接让测试失败

@Test
    @DisplayName("测试快速失败")
    public void shouldFail() {
        if (true){
            fail("测试失败");
        }

    }

4、JUnit5的 假设(assumptions)

假设(assumptions)类似于断言,不同之处在于:

  • 不满足的断言会使测试方法失败
  • 不满足的假设会使测试方法执行终止

所以得出以下总结:假设可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

方法说明
assumeTrue条件为true
assertNotEquals判断两个对象或两个原始类型是否不相等
assertSame判断两个对象引用是否指向同一个对象
assertNotSame判断两个对象引用是否指向不同的对象

5、嵌套测试(@Nested)

Junit5可以通过@Nested注解和java中内部类实现嵌套测试方法,从而更好的把相关的测试组织在一起。
@Nested测试类必须是非静态内部类,并且可以有任意多层的嵌套。不过@BeforeAll和@AfterAll的方法不能直接在@Nested的测试类中使用(除非"per-class”被使用)

示例代码:

@SpringBootTest
@DisplayName("嵌套测试")
public class Nested_test {
    ArrayList<String> list;

    @Test
    @DisplayName("外层")
    void isInstantiatedWithNew(){
//        new ArrayList<>();
        //外层的test方法不能驱动内层中的@BeforeEach和@AfterEach方法
        Assertions.assertNull(list, "list不是空的");
        System.out.println("list为空");
    }

    
    @BeforeAll
    static void beforeAll(){
        System.out.println("所有方法执行之前");
    }

    @AfterAll
    static void afterAll(){
        System.out.println("所有方法执行之后");
    }



    
    //嵌套的第一层
    //再次强调:java不允许内部类中存在static成员,而@Before(After)All是修饰static方法的,所以不允许有@Before(After)All.(除非开启了"per-class"模式)
    @Nested
    @DisplayName("嵌套操作")
    class nestedOperate{
        
        @BeforeEach
        void beforeEach(){
            System.out.println("单个方法调用前");
            list=new ArrayList<String>();
        }

        @AfterEach
        void afterEach(){
            System.out.println("单个方法调用后");
        }

        @Test
        @DisplayName("list是否为空")
        void isEmpty(){
            Assertions.assertTrue(list.isEmpty(), "list不为空");
            System.out.println("list为空");
        }

        //嵌套的第二层
        @Nested
        @DisplayName("list增加一个元素")
        class afterAdd{

            String element = "springboot2好牛逼";

            @BeforeEach
            void pushAnElement(){
                list.add(element);
            }

            /**
             * 内层的Test可以驱动外层的Before(After)Each之类的方法
             */
            @Test
            @DisplayName("list是否为空")
            void isNotEmpty(){
                Assertions.assertFalse(list.isEmpty(),"list为空");
                System.out.println("list不为空");
            }
        }
    }
}

6、参数化测试(@ParameterizedTest)

参数化测试是Junit5中的一个新特性,所以不得不提它。
使用 以下注解,使得测试可以测试多次使用不同的参数值,为我们的单元测试带来了许多便利!

注解说明
@ValueSource为参数化测试指定参数源,支持八大基本数据类型以及String、class类型
@NullSource为参数化测试提供了一个null的参数源
@EnumSource为参数化测试提供一个Enum参数源,该注解提供了一个可选的names参数,你可以用它来指定使用哪些常量,如果忽略,意味着所有常量将被使用
@MethodSource表示指定方法的返回值作为参数化测试的参数源
@CsvSource允许将参数列表定义为以逗号分隔的值(即String类型的值),它使用单引号作为引用字符,一个空的""表示一个空的字符串,而一个’'被当成一个null引用
@CsvFileSource可以使用类路径下的CSV文件,CSV文件中每一行都会触发参数化测试的一次调用
@ArgumentsSource用来指定一个自定义且能够复用的ArgumentsProvider

示例代码:

//@ValueSource示例
@ParameterizedTest
@DisplayName("参数化测试")
@ValueSource(ints = {1,2,3,4,5})
void testParameterized(int i){
     System.out.println(i);
}

//@MethodSource示例
@ParameterizedTest
@DisplayName("参数化测试")
@MethodSource("stringProviders")
void testParameterized(String a){
		System.out.println(a);
}

static Stream<String> stringProviders(){
		return Stream.of("apple","banana","maoge");
}

7、SpringBoot整合JUnit5

想要使用Spring的功能,需要使用@SpringBootTest注解:

@SpringBootTest
class Junti5_test {

    @Test
    void contextLoads() {

    }
}

  • 编写测试方法:@Test(JUnit5版本)
  • JUnit类具有Spring的功能,@Autowired、比如 使用**@Transactional** 标注测试方法,测试完成后自动回滚

8、如何兼容JUnit4

SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入vintage (兼容版本):

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

如果想使用JUnit4的小伙伴想迁移JUnit5,需要注意以下变化:

● 断言在 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。
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

springboot大神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值