0


SpringBoot 单元测试——JUnit5

1、JUnit5概述

1.1、JUnit5 构成

JUnit5 由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

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

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

3)JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,其提供了兼容JUnit4.x,Junit3.x的测试引擎。

1.2、JUnit5 配置

1.2.1、导入 Junit5 开发场景

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

1.2.2、Junit5 开发场景自动导入依赖项

2、JUnit5 使用

2.1、Jnuit5 测试代码开发

2.1.1、测试代码格式

@SpringBootTest注解:添加在需要依赖springboot框架的测试类上,

                                    不然不能使用Springboot的相关开发功能

@Test注解:添加在测试方法上

@SpringBootTest
class Boot05WebAdminApplicationTests {

    @Test
    void contextLoads() {

    }
}

2.1.2、测试样例

@Slf4j
@SpringBootTest
class SpringBootThymeleafApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    DataSource dataSource;

    @Autowired
    UserMapper userMapper;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        Long along = jdbcTemplate.queryForObject("select count(*) from account", long.class);
        log.info("记录总数{}", along);
        log.info("数据源类型{}", dataSource.getClass());
    }

    @Test
    void testUserMapper() {
        User user = userMapper.selectById(1);
        log.info("用户信息{}", user);
    }

    @Test
    void testRedis() {
        ValueOperations<String, String> operations = redisTemplate.opsForValue();

        operations.set("hello", "world");

        String hello = operations.get("hello");
        System.out.println(hello);
    }
}

2.2、JUnit5常用注解

2.2.1、**@Test :**表示方法是测试方法。

@SpringBootTest
class Boot05WebAdminApplicationTests {

    @Test
    void contextLoads() {

    }
}

2.2.2、**@DisplayName :**为测试类或者测试方法设置展示名称

测试代码:

@DisplayName("junit5功能测试")
public class Junit5Test {

    @DisplayName("测试displayname注解")
    @Test
    void testDisplayName() {
        System.out.println(1);
    }
}

输出结果:

2.2.3、**@BeforeEach、@AfterEach、@BeforeAll、@AfterAll **

注解功能:
注解功能
@BeforeEach

表示在每个单元测试之前执行

@AfterEach

表示在每个单元测试之后执行

@BeforeAll

表示在所有单元测试之前执行

@AfterAll

表示在所有单元测试之后执行

测试代码:

@SpringBootTest
@DisplayName("junit5功能测试")
public class Junit5Test {

    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * 测试前置条件
     */
    @DisplayName("测试前置条件")
    @Test
    void testAssumptions() {
        Assumptions.assumeTrue(false, "结果不足true");
        System.out.println("11111");
    }

    @DisplayName("测试displayname注解")
    @Test
    void testDisplayName() {
        System.out.println(1);
    }

    @Disabled
    @DisplayName("测试2")
    @Test
    void test2() {
        System.out.println(2);
        System.out.println(jdbcTemplate.getClass());
    }

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

    @AfterEach
    void testAfterEach() {
        System.out.println("测试就要结束。。。");
    }

    @BeforeAll
    static void testBeforeAll() {
        System.out.println("所有测试就要开始。。。");
    }

    @AfterAll
    static void testAfterAll() {
        System.out.println("所有测试已经结束。。。");
    }
}

输出结果:

2.2.4、**@Disabled :**表示测试类或测试方法不执行

测试代码:

    @Disabled
    @DisplayName("测试2")
    @Test
    void test2() {
        System.out.println(2);
        System.out.println(jdbcTemplate.getClass());
    }

2.2.5、**@Timeout :**表示测试方法运行如果超过了指定时间将会返回错误

测试代码:设置执行时间为500ms,超时报错

    @Test
    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
    void testTimeOut() throws InterruptedException {
        Thread.sleep(520);
    }

返回结果:

2.2.6、**@RepeatedTest :**表示方法可重复执行

测试代码:

    @RepeatedTest(5)    //重复测试5次
    @DisplayName("测试3")
    @Test
    void test3() {
        System.out.println(3);
        System.out.println(jdbcTemplate.getClass());
    }

执行结果:

2.2.7、其他注解

**@ParameterizedTest :**表示方法是参数化测试

** @Tag :**表示单元测试类别

**@ExtendWith :**为测试类或测试方法提供扩展类引用

2.3、Jnuit5 断言(assertions)

  • 检查业务逻辑返回的数据是否合理。
  • 所有的测试运行结束以后,会有一个详细的测试报告;

2.3.1、简单断言

1)简单断言功能:

方法

说明

assertEquals

判断两个对象或两个原始类型是否相等

assertNotEquals

判断两个对象或两个原始类型是否不相等

assertSame

判断两个对象引用是否指向同一个对象

assertNotSame

判断两个对象引用是否指向不同的对象

assertTrue

判断给定的布尔值是否为 true

assertFalse

判断给定的布尔值是否为 false

assertNull

判断给定的对象引用是否为 null

assertNotNull

判断给定的对象引用是否不为 null

2)测试代码:

@SpringBootTest
@DisplayName("junit5功能测试")
public class Junit5Test {
    @DisplayName("测试简单断言")
    @Test
    void testSimpleAssertions() {
        int call = call(1,2);
        assertEquals(2, call, "业务逻辑计算失败");

        Object obj1 = new Object();
        Object obj2 = new Object();
        assertSame(obj1, obj2, "两个对象不一样");
    }

    int call (int x, int y) {
        return x + y;
    }
    }
}

3)测试结果:

** 2.3.2、数组断言**

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

测试代码:

@Test
@DisplayName("array assertion")
public void array() {
 assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}

2.3.3、组合断言

  • assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言- 可以通过 lambda 表达式很容易的提供这些断言- 前边断言失败,后续代码不会执行 - 组合断言,当组合中的所有断言都通过才是true,否则是false

测试代码:

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

2.3.4、异常断言

JUnit5提供了Assertions.assertThrows() ,配合函数式编程就可以进行使用。

    @Test
    @DisplayName("异常断言")
    public void exceptionTest() {
        ArithmeticException exception = Assertions.assertThrows(
                //扔出断言异常
                ArithmeticException.class, () -> System.out.println(1 % 0));

    }

2.3.5、超时断言

Junit5提供了Assertions.assertTimeout() 为测试方法设置了超时时间

@Test
@DisplayName("超时测试")
public void timeoutTest() {
    //如果测试方法时间超过1s将会异常
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

2.3.6、快速失败

  • 当满足某一条件,需要退出测试程序时,可以使用快速失败方法
  • 当调用快速失败方法时,程序会退出,同时输出失败提示
@Test
@DisplayName("fail")
public void shouldFail() {
 fail("This should fail");
}

2.4、前置条件

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

测试代码:

@SpringBootTest
@DisplayName("junit5功能测试")
public class Junit5Test {

    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * 测试前置条件
     */
    @DisplayName("测试前置条件")
    @Test
    void testAssumptions() {
        Assumptions.assumeTrue(false, "结果不足true");
        System.out.println("11111");
    }
}

返回结果:

    1.当不满足前置条件时,后续输出代码未执行

    2.当不满足前置条件时,程序未报错退出,而是终止执行

2.5、嵌套测试

  • JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试
  • 将相关的测试方法组织在一起。
  • 在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。

注意:

1.外层测试方法的执行,不会驱动内层方法:

当内层方法中有 before 或 after 等注解时,调用外层测试方法,并不会驱动其提前或最后执行

2.内层方法的执行,可以驱动外层方法:

当外层方法定义了某一数据结果时,内层测试方法可以直接调用该结构

测试代码:

@DisplayName("嵌套测试")
public class TestingAstackDemo {

    Stack<Object> stack;

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

    @ParameterizedTest
    @DisplayName("参数化方法测试")
    @MethodSource("stringProvider")
    void testParamterized2(String s) {
        System.out.println(s);
    }

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

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
        //在嵌套测试情况下,外层的test不能驱动内层
        //的beforeall等方法,在提前/之后运行
        //内层的test可以驱动外层
//        assertNotNull(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());
            }
        }
    }
}

2.6、参数化测试

1)参数化测试,可以指定测试方法的输入参数

2)参数化测试的注解类型:

注解
功能
@ValueSource

为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型

@NullSource

表示为参数化测试提供一个null的入参

@EnumSource

表示为参数化测试提供一个枚举入参

@CsvFileSource

表示读取指定CSV文件内容作为参数化测试入参

@MethodSource

表示读取指定方法的返回值作为参数化测试入参(方法的返回值为流)

测试代码:

    1)入参为基础类型
    @ParameterizedTest
    @DisplayName("参数化测试")
    @ValueSource(ints = {1, 2, 3, 4, 5})
    void testParamterized(int i) {
        System.out.println(i);
    }
     2)入参为方法返回值类型
    @ParameterizedTest
    @DisplayName("参数化方法测试")
    @MethodSource("stringProvider")
    void testParamterized2(String s) {
        System.out.println(s);
    }

    static Stream<String> stringProvider() {
        return Stream.of("apple", "banana");
    }
标签: spring boot web java

本文转载自: https://blog.csdn.net/weixin_44302046/article/details/124285058
版权归原作者 我的眼里只有学习 所有, 如有侵权,请联系我们删除。

“SpringBoot 单元测试——JUnit5”的评论:

还没有评论