Spring Boot中如何编写优雅的单元测试

这篇具有很好参考价值的文章主要介绍了Spring Boot中如何编写优雅的单元测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单元测试是指对软件中的最小可测试单元进行检查和验证。在Java中,单元测试的最小单元是类。通过编写针对类或方法的小段代码,来检验被测代码是否符合预期结果或行为。执行单元测试可以帮助开发者验证代码是否正确实现了功能需求,以及是否能够适应应用环境或需求变化。

本文将介绍如何在Spring Boot中编写优雅的单元测试,包括如何添加单元测试依赖,如何对不同层次的组件进行单元测试,以及如何使用Mock对象来模拟真实对象行为。本文假设读者已经对Spring Boot和单元测试有一定的了解和基础。

目录

一、Spring Boot中的单元测试依赖

 二、Spring Boot中不同层次的单元测试

service层

Controller层

Repository层

三、Spring Boot中Mock对象的使用

总结


一、Spring Boot中的单元测试依赖

在Spring Boot项目中,要进行单元测试,首先需要添加相应的依赖。如果使用Maven作为构建工具,可以在pom.xml文件中添加以下依赖:


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

这个依赖包含了多个库和功能,主要有以下几个:

  • JUnit:JUnit是Java中最流行和最常用的单元测试框架,它提供了一套注解和断言来编写和运行单元测试。例如@Test注解表示一个测试方法,assertEquals断言表示两个值是否相等。
  • Spring Test:Spring Test是一个基于Spring的测试框架,它提供了一套注解和工具来配置和管理Spring上下文和Bean。例如@SpringBootTest注解表示一个集成测试类,@Autowired注解表示自动注入一个Bean。
  • Mockito:Mockito是一个Java中最流行和最强大的Mock对象库,它可以模拟复杂的真实对象行为,从而简化测试过程。例如@MockBean注解表示创建一个Mock对象,when方法表示定义Mock对象的行为。
  • Hamcrest:Hamcrest是一个Java中的匹配器库,它提供了一套语义丰富而易读的匹配器来进行结果验证。例如assertThat断言表示验证一个值是否满足一个匹配器,is匹配器表示两个值是否相等。
  • AssertJ:AssertJ是一个Java中的断言库,它提供了一套流畅而直观的断言语法来进行结果验证。例如assertThat断言表示验证一个值是否满足一个条件,isEqualTo断言表示两个值是否相等。

除了以上这些库外,spring-boot-starter-test还包含了其他一些库和功能,如JsonPath、JsonAssert、XmlUnit等。这些库和功能可以根据不同的测试场景进行选择和使用。

 二、Spring Boot中不同层次的单元测试

service层

Service层是指封装业务逻辑和处理数据的层,它通常使用@Service或@Component注解来标识。在Spring Boot中,对Service层进行单元测试,可以使用@SpringBootTest注解来加载完整的Spring上下文,从而可以自动注入Service层的Bean。同时,可以使用@MockBean注解来创建和注入其他层次的Mock对象,从而避免真实地调用其他层次的方法,而是模拟其行为。

例如,假设有一个UserService类,它提供了一个根据用户ID查询用户信息的方法:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

要对这个类进行单元测试,可以编写以下测试类:

@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetUserById() {
        // 创建一个User对象
        User user = new User();
        user.setId(1L);
        user.setName("Alice");
        user.setEmail("alice@example.com");

        // 当调用userRepository.findById(1L)时,返回一个包含user对象的Optional对象
        when(userRepository.findById(1L)).thenReturn(Optional.of(user));

        // 调用userService.getUserById方法,传入1L作为参数,得到一个User对象
        User result = userService.getUserById(1L);

        // 验证结果对象与user对象相等
        assertThat(result).isEqualTo(user);

        // 验证userRepository.findById(1L)方法被调用了一次
        verify(userRepository, times(1)).findById(1L);
    }
}

 

在这个测试类中,使用了以下几个关键点和技巧:

  • 使用@SpringBootTest注解表示加载完整的Spring上下文,并使用@Autowired注解将UserService对象注入到测试类中。
  • 使用@MockBean注解表示创建一个UserRepository的Mock对象,并使用@Autowired注解将其注入到测试类中。这样可以避免真实地调用UserRepository的方法,而是模拟其行为。
  • 使用when方法来定义Mock对象的行为,例如当调用userRepository.findById(1L)时,返回一个包含user对象的Optional对象。
  • 使用userService.getUserById方法调用被测方法,得到一个User对象。
  • 使用AssertJ的断言语法来验证结果对象与user对象是否相等。可以使用多种条件和匹配器来验证结果。
  • 使用verify方法来验证Mock对象的方法是否被调用了指定次数。

Controller层

Controller层是指处理用户请求和响应的层,它通常使用@RestController或@Controller注解来标识。在Spring Boot中,对Controller层进行单元测试,可以使用@WebMvcTest注解来启动一个轻量级的Spring MVC上下文,只加载Controller层的组件。同时,可以使用@AutoConfigureMockMvc注解来自动配置一个MockMvc对象,用来模拟Http请求和验证Http响应。

例如,假设有一个UserController类,它提供了一个根据用户ID查询用户信息的接口:

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        if (user == null) {
            return ResponseEntity.notFound().build();
        } else {
            return ResponseEntity.ok(user);
        }
    }
}

要对这个类进行单元测试,可以编写以下测试类:

@WebMvcTest(UserController.class)
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void testGetUserById() throws Exception {
        // 创建一个User对象
        User user = new User();
        user.setId(1L);
        user.setName("Alice");
        user.setEmail("alice@example.com");

        // 当调用userService.getUserById(1L)时,返回user对象
        when(userService.getUserById(1L)).thenReturn(user);

        // 模拟发送GET请求到/users/1,并验证响应状态码为200,响应内容为JSON格式的user对象
        mockMvc.perform(get("/users/1"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.id").value(1L))
                .andExpect(jsonPath("$.name").value("Alice"))
                .andExpect(jsonPath("$.email").value("alice@example.com"));

        // 验证userService.getUserById(1L)方法被调用了一次
        verify(userService, times(1)).getUserById(1L);
    }
}

在这个测试类中,使用了以下几个关键点和技巧:

  • 使用@WebMvcTest(UserController.class)注解表示只加载UserController类的组件,不加载其他层次的组件。
  • 使用@AutoConfigureMockMvc注解表示自动配置一个MockMvc对象,并使用@Autowired注解将其注入到测试类中。
  • 使用@MockBean注解表示创建一个UserService的Mock对象,并使用@Autowired注解将其注入到测试类中。这样可以避免真实地调用UserService的方法,而是模拟其行为。
  • 使用when方法来定义Mock对象的行为,例如当调用userService.getUserById(1L)时,返回user对象。
  • 使用mockMvc.perform方法来模拟发送Http请求,并使用andExpect方法来验证Http响应。可以使用多种匹配器来验证响应状态码、内容类型、内容值等。
  • 使用verify方法来验证Mock对象的方法是否被调用了指定次数。

Repository层

Repository层是指封装数据访问和持久化的层,它通常使用@Repository或@JpaRepository注解来标识。在Spring Boot中,对Repository层进行单元测试,可以使用@DataJpaTest注解来启动一个嵌入式数据库,并自动配置JPA相关的组件。同时,可以使用@TestEntityManager注解来获取一个TestEntityManager对象,用来操作和验证数据库数据。

例如,假设有一个UserRepository接口,它继承了JpaRepository接口,并提供了一个根据用户姓名查询用户列表的方法:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    List<User> findByName(String name);
}

要对这个接口进行单元测试,可以编写以下测试类:

@DataJpaTest
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TestEntityManager testEntityManager;

    @Test
    public void testFindByName() {
        // 创建两个User对象,并使用testEntityManager.persist方法将其保存到数据库中
        User user1 = new User();
        user1.setName("Bob");
        user1.setEmail("bob@example.com");
        testEntityManager.persist(user1);

        User user2 = new User();
        user2.setName("Bob");
        user2.setEmail("bob2@example.com");
        testEntityManager.persist(user2);

        // 调用userRepository.findByName方法,传入"Bob"作为参数,得到一个用户列表
        List<User> users = userRepository.findByName("Bob");

        // 验证用户列表的大小为2,且包含了user1和user2
        assertThat(users).hasSize(2);
        assertThat(users).contains(user1, user2);
    }
}

在这个测试类中,使用了以下几个关键点和技巧:

  • 使用@DataJpaTest注解表示启动一个嵌入式数据库,并自动配置JPA相关的组件。这样可以避免依赖外部数据库,而是使用内存数据库进行测试。
  • 使用@Autowired注解将UserRepository和TestEntityManager对象注入到测试类中。
  • 使用testEntityManager.persist方法将User对象保存到数据库中。这样可以准备好测试数据,而不需要手动插入数据。
  • 使用userRepository.findByName方法调用自定义的查询方法,得到一个用户列表。
  • 使用AssertJ的断言语法来验证用户列表的大小和内容。可以使用多种条件和匹配器来验证结果。

三、Spring Boot中Mock对象的使用

在Spring Boot中,除了使用@WebMvcTest和@DataJpaTest等注解来加载特定层次的组件外,还可以使用@SpringBootTest注解来加载完整的Spring上下文,从而进行更加集成的测试。但是,在这种情况下,可能会遇到一些问题,例如:

        - 测试过程中需要依赖外部资源,如数据库、消息队列、Web服务等。这些资源可能不稳定或不可用,导致测试失败或超时。
        - 测试过程中需要调用其他组件或服务的方法,但是这些方法的实现或行为不确定或不可控,导致测试结果不可预测或不准确。
        - 测试过程中需要验证一些难以观察或测量的结果,如日志输出、异常抛出、私有变量值等。这些结果可能需要使用复杂或侵入式的方式来获取或验证。

为了解决这些问题,可以使用Mock对象来模拟真实对象行为。Mock对象是指在测试过程中替代真实对象的虚拟对象,它可以根据预设的规则来返回特定的值或执行特定的操作。使用Mock对象有以下好处:

        - 降低测试依赖:通过使用Mock对象来替代外部资源或其他组件,可以减少测试过程中对真实环境的依赖,使得测试更加稳定和可靠。
        - 提高测试控制:通过使用Mock对象来模拟特定的行为或场景,可以提高测试过程中对真实对象行为的控制,使得测试更加灵活和精确。
        - 简化测试验证:通过使用Mock对象来返回特定的结果或触发特定的事件,可以简化测试过程中对真实对象结果或事件的验证,使得测试更加简单和直观。

在Spring Boot中,要使用Mock对象,可以使用@MockBean注解来创建和注入一个Mock对象。这个注解会自动使用Mockito库来创建一个Mock对象,并将其添加到Spring上下文中。同时,可以使用when方法来定义Mock对象的行为,以及verify方法来验证Mock对象的方法调用。

例如,假设有一个EmailService接口,它提供了一个发送邮件的方法:

public interface EmailService {

    void sendEmail(String to, String subject, String content);
}

要对这个接口进行单元测试,可以编写以下测试类:

@SpringBootTest
public class EmailServiceTest {

    @Autowired
    private UserService userService;

    @MockBean
    private EmailService emailService;

    @Test
    public void testSendEmail() {
        // 创建一个User对象
        User user = new User();
        user.setId(1L);
        user.setName("Alice");
        user.setEmail("alice@example.com");

        // 当调用emailService.sendEmail方法时,什么也不做
        doNothing().when(emailService).sendEmail(anyString(), anyString(), anyString());

        // 调用userService.sendWelcomeEmail方法,传入user对象作为参数
        userService.sendWelcomeEmail(user);

        // 验证emailService.sendEmail方法被调用了一次,并且参数分别为user.getEmail()、"Welcome"、"Hello, Alice"
        verify(emailService, times(1)).sendEmail(user.getEmail(), "Welcome", "Hello, Alice");
    }
}

在这个测试类中,使用了以下几个关键点和技巧:

  • 使用@SpringBootTest注解表示加载完整的Spring上下文,并使用@Autowired注解将UserService对象注入到测试类中。
  • 使用@MockBean注解表示创建一个EmailService的Mock对象,并使用@Autowired注解将其注入到测试类中。这样可以避免真实地调用EmailService的方法,而是模拟其行为。
  • 使用doNothing方法来定义Mock对象的行为,例如当调用emailService.sendEmail方法时,什么也不做。也可以使用doReturn、doThrow、doAnswer等方法来定义其他类型的行为。
  • 使用anyString方法来表示任意字符串类型的参数。也可以使用anyInt、anyLong、anyObject等方法来表示其他类型的参数。
  • 使用userService.sendWelcomeEmail方法调用被测方法,传入user对象作为参数。
  • 使用verify方法来验证Mock对象的方法是否被调用了指定次数,并且参数是否符合预期。也可以使用never、atLeast、atMost等方法来表示其他次数的验证。

除了使用@MockBean注解来创建和注入Mock对象外,还可以使用@SpyBean注解来创建和注入Spy对象。Spy对象是指在测试过程中部分替代真实对象的虚拟对象,它可以根据预设的规则来返回特定的值或执行特定的操作,同时保留真实对象的其他行为。使用Spy对象有以下好处:

  • 保留真实行为:通过使用Spy对象来替代真实对象,可以保留真实对象的其他行为,使得测试更加接近真实环境。
  • 修改部分行为:通过使用Spy对象来模拟特定的行为或场景,可以修改真实对象的部分行为,使得测试更加灵活和精确。
  • 观察真实结果:通过使用Spy对象来返回特定的结果或触发特定的事件,可以观察真实对象的结果或事件,使得测试更加直观和可信。

在Spring Boot中,要使用Spy对象,可以使用@SpyBean注解来创建和注入一个Spy对象。这个注解会自动使用Mockito库来创建一个Spy对象,并将其添加到Spring上下文中。同时,可以使用when方法来定义Spy对象的行为,以及verify方法来验证Spy对象的方法调用。

例如,假设有一个LogService接口,它提供了一个记录日志的方法:

public interface LogService {

    void log(String message);
}

要对这个接口进行单元测试,可以编写以下测试类:

@SpringBootTest
public class LogServiceTest {

    @Autowired
    private UserService userService;

    @SpyBean
    private LogService logService;

    @Test
    public void testLog() {
        // 创建一个User对象
        User user = new User();
        user.setId(1L);
        user.setName("Alice");
        user.setEmail("alice@example.com");

        // 当调用logService.log方法时,调用真实的方法,并打印参数到控制台
        doAnswer(invocation -> {
            String message = invocation.getArgument(0);
            System.out.println(message);
            invocation.callRealMethod();
            return null;
        }).when(logService).log(anyString());

        // 调用userService.createUser方法,传入user对象作为参数
        userService.createUser(user);

        // 验证logService.log方法被调用了两次,并且参数分别为"Creating user: Alice"、"User created: Alice"
        verify(logService, times(2)).log(anyString());
        verify(logService, times(1)).log("Creating user: Alice");
        verify(logService, times(1)).log("User created: Alice");
    }
}

在这个测试类中,使用了以下几个关键点和技巧:

  • 使用@SpringBootTest注解表示加载完整的Spring上下文,并使用@Autowired注解将UserService对象注入到测试类中。
  • 使用@SpyBean注解表示创建一个LogService的Spy对象,并使用@Autowired注解将其注入到测试类中。这样可以保留LogService的真实行为,同时修改部分行为。
  • 使用doAnswer方法来定义Spy对象的行为,例如当调用logService.log方法时,调用真实的方法,并打印参数到控制台。也可以使用doReturn、doThrow、doNothing等方法来定义其他类型的行为。
  • 使用anyString方法来表示任意字符串类型的参数。也可以使用anyInt、anyLong、anyObject等方法来表示其他类型的参数。
  • 使用userService.createUser方法调用被测方法,传入user对象作为参数。
  • 使用verify方法来验证Spy对象的方法是否被调用了指定次数,并且参数是否符合预期。也可以使用never、atLeast、atMost等方法来表示其他次数的验证。

总结

本文介绍了如何在Spring Boot中编写优雅的单元测试,包括如何添加单元测试依赖,如何对不同层次的组件进行单元测试,以及如何使用Mock对象和Spy对象来模拟真实对象行为。本文还给出了每种类型的单元测试的示例代码,并解释了其中的关键点和技巧。

通过编写单元测试,可以提高Spring Boot应用的质量和稳定性,同时也可以提高开发者的编程水平和信心。希望本文能够对你有所帮助和启发,让你能够在Spring Boot中编写优雅的单元测试。文章来源地址https://www.toymoban.com/news/detail-673059.html

到了这里,关于Spring Boot中如何编写优雅的单元测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Spring Boot集成单元测试之如何mock

    前言 我们在购买电脑的时候,一次就可以开机了,这是因为在出厂的时候厂家就帮我们做好了测试。那如果没有厂家这步,我们会面临显示器无法显示的问题,磁盘无法挂载等情况。那运气好可能一次就能定位,运气不好,还需要排查显示器,内存条,主板,显卡等一系列组

    2024年04月12日
    浏览(25)
  • Spring 官方建议的在 Spring Boot 应用中如何做单元测试

    Spring Boot 提供了丰富的测试功能,主要由以下两个模块组成: ● spring-boot-test:提供测试核心功能。 ● spring-boot-test-autoconfigure:提供对测试的自动配置。 Spring Boot 提供了一个 spring-boot-starter-test一站式启动器,如以下依赖配置所示。 测试启动器依赖不仅包含以上两个 Spring

    2024年02月08日
    浏览(34)
  • 精通Spring Boot单元测试:构建健壮的Java应用

    在当今软件开发领域,单元测试已经成为确保应用质量和可维护性的关键步骤。特别是在Java生态系统中,Spring Boot框架作为一种广泛应用的解决方案,其对于单元测试的支持更是让开发者受益匪浅。本博客的目标是为开发者提供一份清晰易懂的指南,帮助他们利用Spring Boot框

    2024年03月15日
    浏览(44)
  • Java企业级信息系统开发学习笔记(4.2)Spring Boot项目单元测试、热部署与原理分析

    该文章主要为完成实训任务,详细实现过程及结果见【http://t.csdn.cn/pG623】 1. 添加测试依赖启动器和单元测试 修改pom.xml文件,添加依赖 刷新项目依赖 2. 创建测试类与测试方法 在 src/test/java 里创建 cn.kox.boot 包,创建测试类 TestHelloWorld01 给测试类添加测试启动器注解与Spring

    2024年02月10日
    浏览(43)
  • 【Spring Boot】单元测试

    单元测试在日常项目开发中必不可少,Spring Boot提供了完善的单元测试框架和工具用于测试开发的应用。接下来介绍Spring Boot为单元测试提供了哪些支持,以及如何在Spring Boot项目中进行单元测试。 单元测试主要用于测试单个代码组件,以确保代码按预期方式工作。目前流行的

    2024年02月16日
    浏览(38)
  • Spring Boot单元测试

    ❤️❤️❤️SSM专栏更新中,各位大佬觉得写得不错,支持一下,感谢了!❤️❤️❤️ Spring + Spring MVC + MyBatis_冷兮雪的博客-CSDN博客 Spring Boot 中进行单元测试是一个常见的做法,可以帮助你验证应用程序的各个组件是否按预期工作。所以我们有必要去学习一番! 单元测试

    2024年02月13日
    浏览(36)
  • Spring boot 集成单元测试

    1.引入依赖 2.  3.编写测试类

    2024年02月11日
    浏览(41)
  • 【Spring Boot】SpringBoot 单元测试

    单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试。 1、可以⾮常简单、直观、快速的测试某⼀个功能是否正确。 2、使⽤单元测试可以帮我们在打包的时候,发现⼀些问题,因为在打包之前,所以的单元测试必须通过,否则不能打包

    2024年02月07日
    浏览(45)
  • Spring Boot进阶(45): Spring Boot 开发必备技能:如何优雅地返回统一结果包装!

            在实际的开发中,我们常常需要对 API 接口的返回结果进行统一的包装,以方便客户端的处理和异常情况的统一处理。例如,我们可能需要对 API 接口返回结果进行统一的格式处理,将所有的异常情况都转化为统一的错误类型,同时也可能需要对返回结果进行统一的加

    2024年02月07日
    浏览(40)
  • Spring Boot进阶(73):Spring Boot如何优雅地使用Feign进行服务间通信?

            在分布式系统中,服务间通信是非常常见的情况。而Feign就是一个开源的Java HTTP客户端,可以帮助我们在Spring Boot应用中快速构建和使用HTTP客户端,方便实现服务间的通信。本文将介绍如何优雅地使用Feign进行服务间通信。         那么,具体如何实现呢?这将又

    2024年02月06日
    浏览(44)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包