Spring Boot - 用JUnit 5构建完美的Spring Boot测试套件

简介: Spring Boot - 用JUnit 5构建完美的Spring Boot测试套件
package org.junit.jupiter.api;
import static org.apiguardian.api.API.Status.STABLE;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apiguardian.api.API;
import org.junit.platform.commons.annotation.Testable;
/**
 * {@code @Test} is used to signal that the annotated method is a
 * <em>test</em> method.
 *
 * <p>{@code @Test} methods must not be {@code private} or {@code static}
 * and must not return a value.
 *
 * <p>{@code @Test} methods may optionally declare parameters to be
 * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver
 * ParameterResolvers}.
 *
 * <p>{@code @Test} may also be used as a meta-annotation in order to create
 * a custom <em>composed annotation</em> that inherits the semantics of
 * {@code @Test}.
 *
 * <h2>Test Execution Order</h2>
 *
 * <p>By default, test methods will be ordered using an algorithm that is
 * deterministic but intentionally nonobvious. This ensures that subsequent runs
 * of a test suite execute test methods in the same order, thereby allowing for
 * repeatable builds. In this context, a <em>test method</em> is any instance
 * method that is directly annotated or meta-annotated with {@code @Test},
 * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or
 * {@code @TestTemplate}.
 *
 * <p>Although true <em>unit tests</em> typically should not rely on the order
 * in which they are executed, there are times when it is necessary to enforce
 * a specific test method execution order &mdash; for example, when writing
 * <em>integration tests</em> or <em>functional tests</em> where the sequence of
 * the tests is important, especially in conjunction with
 * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}.
 *
 * <p>To control the order in which test methods are executed, annotate your
 * test class or test interface with {@link TestMethodOrder @TestMethodOrder}
 * and specify the desired {@link MethodOrderer} implementation.
 *
 * @since 5.0
 * @see RepeatedTest
 * @see org.junit.jupiter.params.ParameterizedTest
 * @see TestTemplate
 * @see TestFactory
 * @see TestInfo
 * @see DisplayName
 * @see Tag
 * @see BeforeAll
 * @see AfterAll
 * @see BeforeEach
 * @see AfterEach
 */
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@Testable
public @interface Test {
}


JUnit 4 vs JUnit 5


以下是JUnit 4和JUnit 5注解之间的一些主要区别

image.png


这些是JUnit 4和JUnit 5之间的一些重要区别,JUnit 5引入了许多新的功能和改进,以提供更灵活、强大的测试框架。你可以根据项目的需要选择适合的JUnit版本。


Junit5 常用注解


@SpringBootTest: 用于指定测试类启用Spring Boot Test,默认会提供Mock环境。

@ExtendWith: 如果只想启用Spring环境进行简单测试,不想启用Spring Boot环境,可以配置扩展为:SpringExtension。

@Test: 指定方法为测试方法。

@TestMethodOrder: 用于配置测试类中方法的执行顺序策略,配置为OrderAnnotation时,按@Order顺序执行。

@Order: 用于配置方法的执行顺序,数字越低执行顺序越高。

@DisplayName: 用于指定测试类和测试方法的别名。

@BeforeAll: 在测试类的所有测试方法前执行一次,可用于全局初始化。

@AfterAll: 在测试类的所有测试方法后执行一次,可用于全局销毁资源。

@BeforeEach: 在测试类的每个测试方法前都执行一次。

@AfterEach: 在测试类的每个测试方法后都执行一次。

@Disabled: 禁用测试方法。

@RepeatedTest: 指定测试方法重复执行。

@ParameterizedTest: 指定参数化测试方法,类似重复执行,从@ValueSource中获取参数。

@ValueSource: 用于参数化测试指定参数。

@AutoConfigureMockMvc: 启用MockMvc的自动配置,可用于测试接口。



栗子


以下是上述注解的使用方法示例以及相应的Java代码:


  1. @SpringBootTest:用于指定Spring Boot测试。示例:
@SpringBootTest
public class MySpringBootTest {
    // 测试方法
}


  1. @ExtendWith:用于配置测试类的执行环境。示例:
@ExtendWith(SpringExtension.class)
public class MySpringTest {
    // 测试方法
}


  1. @Test:指定方法为测试方法。示例:
@Test
public void testSomeMethod() {
    // 测试逻辑
}


4.@TestMethodOrder@Order:配置测试方法的执行顺序。示例:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderedTestExample {
    @Order(1)
    @Test
    public void testMethod1() {
        // 测试逻辑
    }
    @Order(2)
    @Test
    public void testMethod2() {
        // 测试逻辑
    }
}


5.@DisplayName:用于指定测试类和测试方法的别名。示例:

@DisplayName("My Test Suite")
public class MyTestSuite {
    @Test
    @DisplayName("My Test Case")
    public void myTestCase() {
        // 测试逻辑
    }
}


6.@BeforeAll@AfterAll:在测试类的所有测试方法前和后执行一次,可用于全局初始化和销毁资源。示例:

@BeforeAll
public static void setup() {
    // 初始化操作
}
@AfterAll
public static void teardown() {
    // 资源销毁操作
}


7.@BeforeEach@AfterEach:在测试类的每个测试方法前和后都执行一次。示例:

@BeforeEach
public void beforeEachTest() {
    // 执行前的准备工作
}
@AfterEach
public void afterEachTest() {
    // 执行后的清理工作
}


  1. @Disabled:禁用测试方法。示例:
@Test
@Disabled("This test is not ready yet.")
public void disabledTest() {
    // 未完成的测试逻辑
}


  1. @RepeatedTest:指定测试方法重复执行。示例:
@RepeatedTest(5)
public void repeatedTest() {
    // 该测试方法会重复执行5次
}


10.@ParameterizedTest@ValueSource:用于参数化测试。示例:

@ParameterizedTest
@ValueSource(strings = { "apple", "banana", "cherry" })
public void testFruit(String fruit) {
    // 使用参数化的水果名称进行测试
}


11.@AutoConfigureMockMvc:启用MockMvc的自动配置,可用于测试接口。示例:

@SpringBootTest
@AutoConfigureMockMvc
public class MyControllerIntegrationTest {
    @Autowired
    private MockMvc mockMvc;
    @Test
    public void testController() throws Exception {
        mockMvc.perform(get("/api/someendpoint"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON));
    }
}

这些示例演示了如何使用这些注解来编写JUnit 5和Spring Boot测试。您可以根据您的具体需求和测试场景进行相应的配置和使用。

package com.artisan.boottest.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BasicTest {
    @Test
    public void test() {
        String artisan = "artisan good";
        Assertions.assertEquals("artisan good", artisan);
    }
}
package com.artisan.boottest.example;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
 * @author 小工匠
 * @version 1.0
 * @description: JUnit指定方法测试顺序 
 * @mark: show me the code , change the world
 */
@Slf4j
@ExtendWith(SpringExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MethodOrderTest {
    @Test
    @Order(1)
    @DisplayName("order为1的方法")
    void lowOrder(){
        log.info("lowOrder method");
    }
    @Test
    @Order(2)
    @DisplayName("order为2的方法")
    void highOrder(){
        log.info("highOrder method");
    }
}
package com.artisan.boottest.example;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
 * @author 小工匠
 * @version 1.0
 * @description: JUnit生命周期测试
 * @mark: show me the code , change the world
 */
@Slf4j
@ExtendWith(SpringExtension.class)
public class LifecycleTest {
    @BeforeAll
    static void allInit() {
        log.info("allInit():在所有方法前执行,只执行一次");
    }
    @BeforeEach
    void eachInit() {
        log.info("eachInit():在测试方法前执行,每个测试方法前都执行");
    }
    @Test
    void successTest() {
        log.info("successTest():方法执行成功");
    }
    @AfterEach
    void eachDown() {
        log.info("eachDown():在测试方法后执行,每个测试方法后都执行");
    }
    @AfterAll
    static void allDown() {
        log.info("allDown():在测试方法后执行,每个测试方法后都执行");
    }
}
package com.artisan.boottest.example;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.time.Duration;
/**
 * @author 小工匠
 * @version 1.0
 * @description: JUnit断言测试
 * @mark: show me the code , change the world
 */
@Slf4j
@ExtendWith(SpringExtension.class)
public class AssertTest {
    // 可以使用fail方法直接断言方法执行失败并输出提示信息。
    @Test
    void failTest() {
        Assertions.fail("failTest():方法执行失败");
    }
    //  还可以通过assertTrue、assertNull、assertEquals这类方法来断言结果是否符合预期。
    @Test
    void trueTest(){
        Assertions.assertTrue(666==666);
    }
    @Test
    void trueFalse(){
        Assertions.assertFalse(8888<=9999);
    }
    @Test
    void nullTest(){
        String str = null;
        Assertions.assertNull(str);
    }
    @Test
    void notNullTest(){
        String str = "test";
        Assertions.assertNotNull(str);
    }
    @Test
    void equalsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertEquals(str1,str2);
    }
    @Test
    void notEqualsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertNotEquals(str1,str2);
    }
    // 也可以使用assertThrows方法来断言方法中抛出的异常。
    @Test
    void throwsTest(){
        Assertions.assertThrows(NullPointerException.class,()->{
            String str = null;
            log.info(str.toLowerCase());
        });
    }
    //  还可通过assertTimeout方法断言方法的执行时间。
    @Test
    void timeoutTest(){
        Assertions.assertTimeout(Duration.ofMillis(1000),()->{
            long sleepTime = 2000;
            ThreadUtil.sleep(sleepTime);
            log.info("timeoutTest():休眠{}毫秒",sleepTime);
        });
    }
    // 或者通过assertAll方法将几个断言结合起来使用,Assertions类中提供的工具方法很多,具体可以参考它的代码。
    @Test
    void assertAllTest(){
        Assertions.assertAll(()->{
            trueTest();
        },()->{
            nullTest();
        },()->{
            equalsTest();
        });
    }
}
package com.artisan.boottest.example;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
 * @author 小工匠
 * @version 1.0
 * @description: 其他常用的注释
 * @mark: show me the code , change the world
 */
@Slf4j
@ExtendWith(SpringExtension.class)
public class OthreTest {
    // Spring Boot Test除了上述测试功能,还可以使用@Disabled来禁用某个测试方法
    @Test
    @Disabled("用于测试@Disabled注解")
    void disabledTest() {
        log.info("disabledTest():方法被执行");
    }
    // 也可以使用@RepeatedTest来实现循环测试
    private static int count = 0;
    @RepeatedTest(5)
    void repeatedTest() {
        count++;
        log.info("repeatedTest():重复执行第{}次",count);
    }
    // 还可以通过@ParameterizedTest来进行参数化测试
    @ParameterizedTest
    @ValueSource(ints = {1,2,3})
    public void parameterizedTest(int a){
        log.info("parameterizedTest():a={}",a);
    }
}


【三层测试】

package com.artisan.boottest.project;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
/**
 * @author 小工匠
 * @version 1.0
 * @description: Dao层方法测试
 * @mark: show me the code , change the world
 */
@Slf4j
@SpringBootTest
public class MapperTest {
    @Autowired
    private PmsBrandMapper brandMapper;
    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandMapper.selectByPrimaryKey(id);
        LOGGER.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}
package com.artisan.boottest.project;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
 * @author 小工匠
 * @version 1.0
 * @description: Service层方法测试
 * @mark: show me the code , change the world
 */
@Slf4j
@SpringBootTest
public class ServiceTest {
    @Autowired
    private PmsBrandService brandService;
    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandService.getBrand(id);
        log.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}
package com.artisan.boottest.project;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
/**
 * @author 小工匠
 * @version 1.0
 * @description: 对于Controller层方法进行测试,有时我们需要模拟请求,使用MockMvc即可
 * @mark: show me the code , change the world
 */
@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @Test
    void mvcTest() throws Exception {
        //模拟发送一个请求访问分页查询品牌列表的接口
        mockMvc.perform(MockMvcRequestBuilders.get("/brand/list") //设置请求地址
                        .param("pageNum", "1") //设置请求参数
                        .param("pageSize", "5"))
                .andExpect(MockMvcResultMatchers.status().isOk()) //断言返回状态码为200
                .andDo(MockMvcResultHandlers.print()) //在控制台打印日志
                .andReturn(); //返回请求结果
    }
}


相关文章
|
1月前
|
安全 前端开发 Java
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择。依赖注入使对象管理交由Spring容器处理,实现低耦合高内聚;AOP则分离横切关注点如事务管理,增强代码模块化。Spring还提供MVC、Data、Security等模块满足多样需求,并通过Spring Boot简化配置与部署,加速微服务架构构建。掌握这些核心概念与工具,开发者能更从容应对挑战,打造卓越应用。
34 1
|
1月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
14天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
15天前
|
JavaScript 前端开发 Java
Spring Boot+cucumber+契约测试
Spring Boot+cucumber+契约测试
10 0
Spring Boot+cucumber+契约测试
|
15天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
26天前
|
前端开发 安全 Java
技术进阶:使用Spring MVC构建适应未来的响应式Web应用
【9月更文挑战第2天】随着移动设备的普及,响应式设计至关重要。Spring MVC作为强大的Java Web框架,助力开发者创建适应多屏的应用。本文推荐使用Thymeleaf整合视图,通过简洁的HTML代码提高前端灵活性;采用`@ResponseBody`与`Callable`实现异步处理,优化应用响应速度;运用`@ControllerAdvice`统一异常管理,保持代码整洁;借助Jackson简化JSON处理;利用Spring Security增强安全性;并强调测试的重要性。遵循这些实践,将大幅提升开发效率和应用质量。
53 7
|
22天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
54 2
|
1月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
28天前
|
Java 微服务 Spring
驾驭复杂性:Spring Cloud在微服务构建中的决胜法则
【8月更文挑战第31天】Spring Cloud是在Spring Framework基础上打造的微服务解决方案,提供服务发现、配置管理、消息路由等功能,适用于构建复杂的微服务架构。本文介绍如何利用Spring Cloud搭建微服务,包括Eureka服务发现、Config Server配置管理和Zuul API网关等组件的配置与使用。通过Spring Cloud,可实现快速开发、自动化配置,并提升系统的伸缩性和容错性,尽管仍需面对分布式事务等挑战,但其强大的社区支持有助于解决问题。
42 0
|
28天前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
36 0