SpringBoot单元测试之常见框架和注解

这篇具有很好参考价值的文章主要介绍了SpringBoot单元测试之常见框架和注解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Mock的概念

在软件开发中提及"mock",通常理解为模拟对象。它可以用来对系统、组件或类进行隔离。在测试过程中,我们通常关注测试对象本身的功能和行为,而对测试对象涉及的一些依赖,仅仅关注它们与测试对象之间的交互(比如是否调用、何时调用、调用的参数、调用的次数和顺序,以及返回的结果或发生的异常等),并不关注这些被依赖对象如何执行这次调用的具体细节。因此,Mock 机制就是使用 Mock 对象替代真实的依赖对象,并模拟真实场景来开展测试工作。

使用 Mock 对象完成依赖关系测试的示意图如下所示:

SpringBoot单元测试之常见框架和注解

 

SpringBootTest包导入的组件

比如 JUnit、JSON Path、AssertJ、Mockito、Hamcrest 等,这里我们有必要对这些组件进行展开说明。

  • JUnit:JUnit 是一款非常流行的基于 Java 语言的单元测试框架,在我们的课程中主要使用该框架作为基础的测试框架。
  • JSON Path:类似于 XPath 在 XML 文档中的定位,JSON Path 表达式通常用来检索路径或设置 JSON 文件中的数据。
  • AssertJ:AssertJ 是一款强大的流式断言工具,它需要遵守 3A 核心原则,即 Arrange(初始化测试对象或准备测试数据)——> Actor(调用被测方法)——>Assert(执行断言)。
  • Mockito:Mockito 是 Java 世界中一款流行的 Mock 测试框架,它主要使用简洁的 API 实现模拟操作。在实施集成测试时,我们将大量使用到这个框架。
  • Hamcrest:Hamcrest 提供了一套匹配器(Matcher),其中每个匹配器的设计用于执行特定的比较操作。
  • JSONassert:JSONassert 是一款专门针对 JSON 提供的断言框架。
  • Spring Test & Spring Boot Test:为 Spring 和 Spring Boot 框架提供的测试工具。

Spring单元测试编写

大致可以分为如下三类:

  1. 单元测试:一般面向方法,编写一般业务代码时,测试成本较大。涉及到的注解有@Test
  2. 切片测试:一般面向难于测试的边界功能,介于单元测试和功能测试之间。涉及到的注解有@RunWith@WebMvcTest等。
  3. 功能测试:一般面向某个完整的业务功能,同时也可以使用切面测试中的mock能力,推荐使用。涉及到的注解有@RunWith@SpringBootTest等。

初始化测试环境

@SpringBootTest
@RunWith(SpringRunner.class)
复制代码

@SpringBootTest注解

默认情况下,@SpringBootTest不会启动嵌入式的服务器。您可以使用 @SpringBootTest 的 webEnvironment 属性进一步完善测试的运行方式:

  • MOCK: 加载 WebApplicationContext 并提供一个 Mock 的 Servlet 环境,此时内置的 Servlet 容器并没有正式启动,可以配合@AutoConfigureMockMvc@AutoConfigureWebTestClient结合使用。
    • 在多数场景下,一个真实的 Servlet 环境对于测试而言过于重量级,通过 MOCK 环境则可以缓解这种环境约束所带来的成本和挑战。
  • RANDOM_PORT: 加载 EmbeddedWebApplicationContext 并提供一个真实的 Servlet 环境,然后使用一个随机端口启动内置容器。
  • DEFINED_PORT: 这个配置也是通过加载 EmbeddedWebApplicationContext 提供一个真实的 Servlet 环境,但使用的是默认端口,如果没有配置端口就使用 8080。
  • NONE: 加载 ApplicationContext 但并不提供任何真实的 Servlet 环境

使用命令行参数

如果您的应用程序需要arguments,您可以使用@SpringBootTestargs属性注入它们

@SpringBootTest(args = "--app.test=one")
class ApplicationArgumentsExampleTests {

    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }

}
复制代码

常见测试注解总结

SpringBoot单元测试之常见框架和注解

 

自动配置类型的注解(@AutoConfigure*)

  1. @AutoConfigureJsonTesters 自动配置JsonTester
  2. @AutoConfigureTestEntityManager 自动配置TestEntityManager
  3. @AutoConfigureMockRestServiceServer 自动配置 MockRestServiceServer
  4. @AutoConfigureWebTestClient 自动配置 WebTestClient
  5. @AutoConfigureMockMvc 自动配置 MockMvc
  6. @AutoConfigureTestDatabase 自动配置Test Database,可以使用内存数据库

事务回滚@Transactional

单独的@Transactional是回滚事务,在添加@Transactional的情况下如果要提交事务,只需要增加@Rollback(false);另外由于@Rollback可以用在方法上,所以一个测试类中,我们可以实现部分测试方法用@Rollback回滚事务,部分测试方法用@Rollback(false)来提交事务。

TestRestTemplate、WebTestClient 和 MockMvc 有什么区别?

虽然这三位候选者都服务于相似的目标:调用我们的 HTTP 端点并验证响应

MockMvc 与模拟 servlet 环境交互的 Fluent API,有支持验证服务器端渲染视图端点的模型或视图名称的 API。

    • 使用MockMvc,我们不需要启动我们的嵌入式 servlet 容器(例如Tomcat)。因此我们不占用任何端口。我们使用该MockMvc实例与这个模拟环境交互,而不启动真正的 HTTP 通信。

WebTestClient 最初是用于调用和验证 Spring WebFlux 端点的测试工具。然而,我们也可以使用它为正在运行的servlet容器或MockMvc编写测试。

TestRestTemplate 通过HTTP测试和验证正在运行的servlet容器的控制器端点,API不太流畅。

SpringBoot单元测试之常见框架和注解

 

MockMvc工具类

MockMvc 类提供的基础方法分为以下 6 种,下面一一对应来看下。

  • Perform:执行一个 RequestBuilder 请求,会自动执行 SpringMVC 流程并映射到相应的 Controller 进行处理。
  • get/post/put/delete:声明发送一个 HTTP 请求的方式,根据 URI 模板和 URI 变量值得到一个 HTTP 请求,支持 GET、POST、PUT、DELETE 等 HTTP 方法。
  • param:添加请求参数,发送 JSON 数据时将不能使用这种方式,而应该采用 @ResponseBody 注解。
  • andExpect:添加 ResultMatcher 验证规则,通过对返回的数据进行判断来验证 Controller 执行结果是否正确。
  • andDo:添加 ResultHandler 结果处理器,比如调试时打印结果到控制台。
  • andReturn:最后返回相应的 MvcResult,然后执行自定义验证或做异步处理。

测试案例

  @Test
  void pageMessageCenterByUserId(@Autowired MockMvc mvc) throws Exception {
    MvcResult mvcResult = mvc.perform(get("xxx")
      // 请求数据类型
      .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
      // 返回数据类型
      .accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
      // session会话对象
      .session(session)
      // URL传参
      .param("key", "value")
      // body传参
      .content(json))
      // 验证参数
      .andExpect(status().isOk())
      .andExpect(jsonPath("$.code").value(0))
      // 打印请求和响应体
      .andDo(MockMvcResultHandlers.print());
    // 打印响应body
    System.out.println(mvcResult.getResponse().getContentAsString());
  }
复制代码

测试下载文件

使用mockMvc测试下载文件时,需要注意controller方法的返回值需要为void,否则会报HttpMessageNotWritableException的异常错误

@Test
@WithUserDetails("admin")
@DisplayName("测试下载excel文件")
void downExcel() throws Exception {
    mockMvc.perform(get("/system/operate/export/excel")
                    .accept(MediaType.APPLICATION_OCTET_STREAM)
                    .param("startTime", "2022-11-22 10:51:25")
                    .param("endTime", "2022-11-23 10:51:25"))
    .andExpect(status().isOk())
    .andDo((result) -> {
        String contentDisposition = result.getResponse().getHeader("Content-Disposition");
        String fileName = URLDecoder.decode(contentDisposition.split("=")[1]);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(result.getResponse().getContentAsByteArray());
        String basePath = System.getProperty("user.dir");
        // 保存为文件
        File file = new File(basePath + "/" + fileName);
        FileUtil.del(file);
        FileOutputStream outputStream = new FileOutputStream(file);
        StreamUtils.copy(inputStream, outputStream);
        outputStream.close();
        inputStream.close();
    });
}
复制代码

SpringSecurity 单元测试

Spring Security 也提供了专门用于测试安全性功能的 spring-security-test 组件,如下所示:

<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-test</artifactId>
     <scope>test</scope>
</dependency>
复制代码

测试用户/认证

使用@WithMockUser@WithUserDetails@WithAnonymousUser等注解

@WithAnonymousUser是用来模拟一种特殊的用户,也被叫做匿名用户。如果有测试匿名用户的需要,可以直接使用该注解。

@WithMockUser注解可以帮我们在Spring Security安全上下文中模拟一个用户。

  • 虽然 @WithMockUser是一种非常方便的方式,但可能并非在所有情况下都凑效。有时候你魔改了一些东西使得安全上下文的验证机制发生了改变,比如你定制了UserDetails,这一类注解就不好用了。但是通过UserDetailsService 加载的用户往往还是可靠的。

@WithUserDetails就派上了用场,它会根据传入的用户名调用UserDetailsService 的loadUserByUsername方法查找用户并加载到安全上下文中

测试 CSRF

下述 csrf() 方法的作用就是在请求中添加 CSRF Token

@Test
public void testHelloUsingPOSTWithCSRF() throws Exception {
     mvc.perform(post("/hello").with(csrf()))
            .andExpect(status().isOk());
}
复制代码

测试CORS

我们通过 MockMvc 发起请求,然后对响应的消息头进行验证即可,测试用例如下所示:

@SpringBootTest
@AutoConfigureMockMvc
public class MainTests {
 
    @Autowired
    private MockMvc mvc;
 
    @Test
    public void testCORSForTestEndpoint() throws Exception {
        mvc.perform(options("/hello")
                .header("Access-Control-Request-Method", "POST")
                .header("Origin", "http://www.test.com")
        )
        .andExpect(header().exists("Access-Control-Allow-Origin"))
        .andExpect(header().string("Access-Control-Allow-Origin", "*"))
        .andExpect(header().exists("Access-Control-Allow-Methods"))
        .andExpect(header().string("Access-Control-Allow-Methods", "POST"))
        .andExpect(status().isOk());
    }
}
复制代码

可以看到,针对 CORS 配置,我们分别获取了响应结果的"Access-Control-Allow-Origin"和"Access-Control-Allow-Methods"消息头并进行了验证。

SpringBoot加载测试专用属性

用args添加临时命令行参数

@SpringBootTest(args = {"--test.prop=test"})
复制代码

激活指定配置文件

@ActiveProfiles("pro")
复制代码

加载其他配置文件

@TestPropertySource(locations = "classpath:config-test.properties")
复制代码

常见问题

Junit版本问题

如果使用的是JUnit 4,需要添加@RunWith(SpringRunner.class)到测试中,否则会报错。如果您使用的是JUnit 5,则无需添加,因为@SpringBootTest中已经添加了@ExtendWith(SpringExtension.class),测试类中不需要在写@Runwith的时候,可以在pom中排除junit4的依赖。

断言

JUnit5 断言使用org.junit.jupiter.api.Assertions的静态方法。 除此之外还可以使用 AssertJ(org.assertj.core.api.AssertionsassertThat方法)。

单元测试中@Transactional不回滚的问题

docs.spring.io/spring-boot…

If your test is @Transactional, it rolls back the transaction at the end of each test method by default. However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, the HTTP client and server run in separate threads and, thus, in separate transactions. Any transaction initiated on the server does not roll back in this case.

如果您的测试是@Transactional,默认情况下,它会在每个测试方法结束时回滚事务。然而,使用RANDOM_PORT或DEFINED_PORT的这种提供了一个真正的servlet环境的情况下,HTTP客户端和服务器在单独的线程中运行,因此会在单独的事务中运行。这种情况下,服务器上发起的任何事务都不会回滚。

you should use caution if Spring-managed or application-managed transactions are configured with any propagation type other than REQUIRED or SUPPORTS (see the discussion on transaction propagation for details).

如果Spring管理的或应用管理的事务被配置为REQUIRED或SUPPORTS以外的任何传播类型,你应该谨慎行事,因为它们都需要遵循事务的传播方式,也会出现事务不会滚的问题,比如你用了REQUIRED_NEW的话就跟单元测试中的事务不在一个事务中了,所以无法回滚。文章来源地址https://www.toymoban.com/news/detail-445633.html

到了这里,关于SpringBoot单元测试之常见框架和注解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot单元测试--Mockito+Junit5框架使用

    作为程序员为了提前发现代码bug,优化代码; 通常我们写完某个功能模块代码后都需要写单元测试对代码块进行测试(特别是敏捷开发中);Java项目最常用的单元测试框架即为Junit(目前最新版本为Junit5),SpringBoot本身也整合了该框架。在写单元测试时代码块中的调到第三方接口方

    2024年02月02日
    浏览(47)
  • JAVA学习-注解.基于注解的单元测试

            基于注解的单元测试是一种使用注解来简化和增强测试代码编写和执行的方法。在Java中,有多个基于注解的单元测试框架可供选择,包括JUnit、TestNG等。下面将对几个常见的基于注解的单元测试框架进行概述,并介绍它们的特点、使用方法以及与其他框架的比较。

    2024年04月28日
    浏览(36)
  • 单元测试 - 注解篇

    1. @RunWith 指定单测的运行环境 @RunWith(JUnit4.class) - JUnit4环境 @RunWith(MockitoJUnitRunner.class) - Mock环境 @RunWith(SpringJUnit4ClassRunner.class) / @RunWith(SpringRunner.class) - Spring环境  ps: SpringJUnit4ClassRunner 与 SpringRunner区别 SpringRunner继承自SpringJUnit4ClassRunner,无额外扩展 junit 4.12之前的版本只能用

    2024年02月02日
    浏览(30)
  • 单元测试&反射&注解

             就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。             可以用来对方法进行测试,它是由Junit公司开源出来的            反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。             

    2024年02月07日
    浏览(39)
  • 【JAVA】单元测试、反射、注解、动态代理

    @Test 测试方法 @Before 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 @After 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 @Before Class 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 @After Class 用来静态修饰方法,该方法

    2024年02月11日
    浏览(42)
  • Java高级技术:单元测试、反射、注解

    目录 单元测试 单元测试概述 单元测试快速入门 单元测试常用注解 反射 反射概述 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射的作用-绕过编译阶段为集合添加数据 反射的作用-通用框架的底层原理 注解 注解概述 自定义注解 元注解

    2024年01月16日
    浏览(51)
  • idea中添加单元测试@Test注解引入

    添加依赖,使用 @Test 即可。 @Test注解找不到或没有的原因: 我们要进行项目的某部分的test测试,会放在名为test的文件夹下,而现在我们的文件夹测试并不是test下面的一个测试,所以如果我们希望在别的地方也可以利用test注解,只需要去掉scope这一行就行了。

    2024年02月22日
    浏览(42)
  • 面向对象(高级)-Annotation注解、单元测试的使用

    注解大纲 什么是注解 注解(Annotation)是从 JDK5.0 开始引入,以** @注解名 **在代码中存在。例如: ​ Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在Annotation的\\\"name = value\\\"对

    2023年04月24日
    浏览(43)
  • Spring面试题--Spring框架常见注解

    Spring 的常见注解有哪些? 注解 说明 @Component 、 @Controller 、 @Service 、 @Repository 使用在类上用于实例化 Bean @Autowired 使用在字段上用于根据类型依赖注入 @Qualifier 结合 @Autowired 一起使用用于根据名称进行依赖注入 @Scope 标注 Bean 的作用范围 @Configuration 指定当前类是一个 配置类

    2024年02月12日
    浏览(46)
  • 【Java实用干货】使用@SpringBootTest注解进行单元测试

    【【Java实用干货】使用@SpringBootTest注解进行单元测试 大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得海量学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群~ @SpringBootTest注解是SpringBoot自1.4.0版本开始引入的

    2024年02月05日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包