首页 > 其他分享 >单元测试实战(二)Service 的测试

单元测试实战(二)Service 的测试

时间:2023-12-07 19:24:53浏览次数:35  
标签:实战 given Service u1 单元测试 repo User 测试 import

单元测试实战(二)Service 的测试

为鼓励单元测试,特分门别类示例各种组件的测试代码并进行解说,供开发人员参考。

本文中的测试均基于JUnit5

单元测试实战(一)Controller 的测试

单元测试实战(二)Service 的测试

单元测试实战(三)JPA 的测试    

单元测试实战(四)MyBatis-Plus 的测试

单元测试实战(五)普通类的测试

单元测试实战(六)其它

概述

与Controller不同,Service的测试可以脱离Spring上下文环境。这是因为Controller测试需要覆盖从HTTP请求到handler方法的路由,即需要SpringMvc的介入;而Service则是一种比较单纯的类,可以当做简单对象来测试。

我们将使用JUnit的MockitoExtension扩展来对Service对象进行测试。待测试对象为测试类的一个属性。测试仍遵循经典三段式:given、when、then;即:假设xxx……那么当yyy时……应该会zzz。

在每个测试之前应清理/重置测试数据,即操作的业务实体。

断言应主要检查Service的行为是否符合预期。

依赖

需要的依赖与Controller测试需要的依赖相同:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-test</artifactId>
  4. <scope>test</scope>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.junit.jupiter</groupId>
  8. <artifactId>junit-jupiter-api</artifactId>
  9. <scope>test</scope>
  10. </dependency>

示例

以下是UserService的实现类UserServiceImpl。接口定义省略(从@Override注解不难推出)。

  1. package com.aaa.api.auth.service.impl;
  2. import com.aaa.api.auth.entity.User;
  3. import com.aaa.api.auth.repository.UserRepository;
  4. import com.aaa.api.auth.service.UserService;
  5. import org.springframework.stereotype.Service;
  6. import java.time.Instant;
  7. import java.util.List;
  8. @Service
  9. public class UserServiceImpl implements UserService {
  10. private final UserRepository repo;
  11. public UserServiceImpl(UserRepository repo) {
  12. this.repo = repo;
  13. }
  14. @Override
  15. public User findById(Long id) {
  16. return repo.findById(id).orElse(null);
  17. }
  18. @Override
  19. public User findByUserCode(String userCode) {
  20. return repo.findByUserCode(userCode).orElse(null);
  21. }
  22. @Override
  23. public User save(User user) {
  24. user.setGmtModified(Instant.now());
  25. return repo.save(user);
  26. }
  27. @Override
  28. public List<User> findAll() {
  29. return repo.findAll();
  30. }
  31. }

以下是对UserServiceImpl进行测试的测试类:

  1. package com.aaa.api.auth.service;
  2. import com.aaa.api.auth.entity.User;
  3. import com.aaa.api.auth.repository.UserRepository;
  4. import com.aaa.api.auth.service.impl.UserServiceImpl;
  5. import org.junit.jupiter.api.BeforeEach;
  6. import org.junit.jupiter.api.Test;
  7. import org.junit.jupiter.api.extension.ExtendWith;
  8. import org.mockito.InjectMocks;
  9. import org.mockito.Mock;
  10. import org.mockito.junit.jupiter.MockitoExtension;
  11. import java.util.List;
  12. import java.util.Optional;
  13. import static org.assertj.core.api.Assertions.assertThat;
  14. import static org.junit.jupiter.api.Assertions.assertThrows;
  15. import static org.mockito.ArgumentMatchers.any;
  16. import static org.mockito.BDDMockito.given;
  17. @ExtendWith(MockitoExtension.class)
  18. class UserServiceTest {
  19. @Mock
  20. private UserRepository repo;
  21. @InjectMocks
  22. private UserServiceImpl svc;
  23. private final User u1 = new User();
  24. private final User u2 = new User();
  25. private final User u3 = new User();
  26. @BeforeEach
  27. void setUp() {
  28. u1.setName("张三");
  29. u1.setUserCode("zhangsan");
  30. u1.setRole(User.ADMIN);
  31. u1.setEmail("[email protected]");
  32. u1.setMobile("13600001234");
  33. u2.setName("李四");
  34. u2.setUserCode("lisi");
  35. u2.setRole(User.ADMIN);
  36. u2.setEmail("[email protected]");
  37. u2.setMobile("13800001234");
  38. u3.setName("王五");
  39. u3.setUserCode("wangwu");
  40. u3.setRole(User.USER);
  41. u3.setEmail("[email protected]");
  42. u3.setMobile("13900001234");
  43. }
  44. @Test
  45. void testFindById() {
  46. // given - precondition or setup
  47. given(repo.findById(1L)).willReturn(Optional.of(u1));
  48. // when - action or the behaviour that we are going test
  49. User found = svc.findById(1L);
  50. // then - verify the output
  51. assertThat(found).isNotNull();
  52. assertThat(found.getUserCode()).isEqualTo("zhangsan");
  53. }
  54. @Test
  55. void testFindByIdNegative() {
  56. // given - precondition or setup
  57. given(repo.findById(1L)).willReturn(Optional.empty());
  58. // when - action or the behaviour that we are going test
  59. User found = svc.findById(1L);
  60. // then - verify the output
  61. assertThat(found).isNull();
  62. }
  63. @Test
  64. void testFindByUserCode() {
  65. // given - precondition or setup
  66. given(repo.findByUserCode(any())).willReturn(Optional.of(u1));
  67. // when - action or the behaviour that we are going test
  68. User found = svc.findByUserCode("zhangsan");
  69. // then - verify the output
  70. assertThat(found).isNotNull();
  71. assertThat(found.getUserCode()).isEqualTo("zhangsan");
  72. }
  73. @Test
  74. void testFindByUserCodeNegative() {
  75. // given - precondition or setup
  76. given(repo.findByUserCode(any())).willReturn(Optional.empty());
  77. // when - action or the behaviour that we are going test
  78. User found = svc.findByUserCode("zhangsan");
  79. // then - verify the output
  80. assertThat(found).isNull();
  81. }
  82. @Test
  83. void testSave() {
  84. // given - precondition or setup
  85. given(repo.save(any(User.class))).willAnswer((invocation -> invocation.getArguments()[0]));
  86. // when - action or the behaviour that we are going test
  87. User saved = svc.save(u1);
  88. // then - verify the output
  89. assertThat(saved).isNotNull();
  90. assertThat(saved.getGmtModified()).isNotNull();
  91. }
  92. @Test
  93. void testSaveNegative() {
  94. // given - precondition or setup
  95. given(repo.save(any())).willThrow(new RuntimeException("Testing"));
  96. // when - action or the behaviour that we are going test
  97. // User saved = svc.save(u1);
  98. // then - verify the output
  99. assertThrows(RuntimeException.class, () -> svc.save(u1));
  100. }
  101. @Test
  102. void testFindAll() {
  103. // given - precondition or setup
  104. given(repo.findAll()).willReturn(List.of(u1, u2, u3));
  105. // when - action or the behaviour that we are going test
  106. List<User> found = svc.findAll();
  107. // then - verify the output
  108. assertThat(found).isNotNull();
  109. assertThat(found.size()).isEqualTo(3);
  110. }
  111. }

测试类说明:

第22行,我们使用了JUnit的MockitoExtension扩展。

第26行,我们Mock了一个UserRepository类型的对象repo,它是待测UserServiceImpl对象的依赖。由于脱离了Spring环境,所以它是个@Mock,不是@MockBean。

接着,第29行,就是待测对象svc。它有个注解@InjectMocks,意思是为该对象进行依赖注入(Mockito提供的功能);于是,repo就被注入到svc里了。

第31-33行提供了三个测试数据,并在setUp()方法中进行初始化/重置。@BeforeEach注解使得setUp()方法在每个测试之前都会执行一遍。

接下来,从56行开始,是测试方法;每个方法都遵循given - when - then三段式。

testFindById方法是测试根据id获取User对象的。它假设repository的findById(1)会返回对象u1;那么当调用svc.findById(1)时;返回的实体就应该是u1。

testFindByIdNegative方法是根据id获取User对象的负面测试。它假设找不到ID为1的User,即repository的findById(1)会返回空;那么当调用svc.findById(1)时;返回的实体应该为空。

testFindByUserCode、testFindByUserCodeNegative与testFindById、testFindByIdNegative一样,只不过查询条件换成userCode,不再赘述。

testSave方法是测试保存User对象的。它假设repository的save()方法在保存任何User对象时都会返回该对象本身;那么当调用svc.save(u1)时;返回的实体应该为u1。注意在这里我们assert了gmtModified属性,以确认UserServiceImpl.save()方法里对该属性的设置。

testSaveNegative方法是保存User对象的负面测试。它假设repository的save()方法会抛出运行时异常;那么当调用svc.save(u1)时;会接到这个异常。

testFindAll方法是测试获取所有User对象的,它假设repository的findAll()会返回对象u1、u2、u3;那么当调用svc.findAll()时;就应返回全部三个对象。

总结

Service的测试,推荐使用@ExtendWith(MockitoExtension.class),脱离Spring上下文,使用纯Mockito打桩。其它方面,理念均与Controller测试一样。

虽然Service测试的打桩器较简单,但由于业务逻辑可能都位于这一层,需要覆盖的场景多,测试用例也应该多。Service层的测试是所有层中最重要的。

原文链接:https://blog.csdn.net/ioriogami/article/details/134481160

标签:实战,given,Service,u1,单元测试,repo,User,测试,import
From: https://www.cnblogs.com/sunny3158/p/17883750.html

相关文章

  • Java编写单元测试用例
    Java编写单元测试用例01简介Java中可以使用多个测试框架来执行单元测试,其中最流行的测试框架之一是JUnit。下面是一个简单的示例,说明如何使用JUnit框架编写和执行单元测试:02一、JUnit包导入importorg.junit.*;03二、创建测试类这个类需要使用“@RunWith’和@Suite”注......
  • Java单元测试浅析(JUnit+Mockito)
    Java单元测试浅析(JUnit+Mockito)作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作。2)集成测试:在单元测试基础上,整合各个模......
  • java 单元测试之 - Spring框架提供的单元/集成测试注解
    java单元测试之-Spring框架提供的单元/集成测试注解Spring框架提供了很多注解来辅助完成单元测试和集成测试(备注:这里的集成测试指容器内部的集成测试,非系统间的集成测试),先看看Spring框架提供了哪些注解以及对应的作用。@RunWith(SpringRunner.class)/@ExtendWith(Spring......
  • Java Mockito 快速入门指南 Mock是指使用Mockito创建的模拟对象,它模拟真实对象的行为,
    JavaMockito快速入门指南Mock是指使用Mockito创建的模拟对象,它模拟真实对象的行为,用于替代真实对象的依赖项,以便进行独立的单元测试在软件开发中,单元测试对于确保代码的正确性和可靠性至关重要。Mockito是一个强大的Java测试框架,它提供了丰富的功能和方法,使得编写模拟测试变得......
  • 单元测试平台搭建:sonarQube+sonarScanner+Jenkins+jacoco
    单元测试平台搭建及结果分析一、方案需求目标:提高单元测试覆盖率和规范代码编写规范选用工具:Sonarqube、sonarqubeScanner、Jenkins、jacoco方案:工程中引入jacoco进行代码覆盖率统计,通过sonarqubescanner扫描工程编写规范,编写单元测试代码后,结合Jenkins每次的编译部署自动......
  • vs+xunit 单元测试
    vs中nuget安装Microsoft.NET.Test.Sdk、xunit、xunit.runner.visualstudioxunit测试demopublicclassHelloTest{//xunit提供默认输出接口privatereadonlyITestOutputHelper_output;publicHelloTest(ITestOutputHelperoutput){_outp......
  • nginx:Failed to start nginx.service:unit not found
    nginx:Failedtostartnginx.service:unitnotfound-知乎(zhihu.com)错误的原因就是没有添加nginx服务,所以启动失败。解决方法:1. 在/root/etc/init.d/目录下新建文件,文件名为nginx或者用命令在根目录下执行:#vim/etc/init.d/nginx(注意vim旁边有一个空格)2.插入......
  • 实战最新鸿蒙系统的短视频App:教你一步步掌握HarmonyOS的几个要点
    鸿蒙(HarmonyOS)是华为公司推出的全场景分布式操作系统,具备高效、安全和灵活的特性。本文将分享如何实战鸿蒙系统的智能家居控制App,并教你一步步掌握HarmonyOS的关键要点,并提供相关示例代码。一、了解HarmonyOS的核心概念在实战HarmonyOS智能家居控制App之前,首先需要了解HarmonyOS的......
  • 微服务(Microservices)
    微服务(Microservices)出处本文是于2014年由JamesLewis和MartinFowler共同编写的文章-微服务(Microservices)的译文原文网址:https://martinfowler.com/articles/microservices.html前言"微服务架构"这个术语在过去几年中频繁被提到,用来描述一种将软件应用程序设计为可独立......
  • @SpringBootTest 和 @RunWith 注解不能识别 单元测试第一步引入maven依赖
    @SpringBootTest和@RunWith注解不能识别单元测试第一步引入maven依赖一、背景    最近在预研 Rocketmq,在写小例子的时候,需要编写测试代码,突然间发现我的 @SpringBootTest 和 @RunWith 这两个注解不能识别,于是展开了我的问题排查过程。问题截图如下:二、问题排......