Spring单元测试(一)入门与实践

问题:如何快速测试,而不是每次测试都要重启应用?

目标:尽量只测试局部代码

不同的测试

软件工程中分为:单元测试、集成测试、功能测试、系统测试。其中功能测试和系统测试一般是测试人员的责任,但单元测试和集成测试则必须由开发人员保证。

  • 功能测试:检查是否满足需求说明书中确定的各项需求。
  • 系统测试:与系统其他功能组合在一起进行测试
  • 单元测试:检查开发者编写的一小段代码,以确认改动是否正确。
    一个功能往往会调用多个接口方法实现,在单元测试时,需要屏蔽掉这些外在接口方法的依赖,将测试焦点集中到目标功能代码上。比如改动了底层工具类 DateUtils 的公共方法。

这时模拟就是最有力的工具,模拟的是功能的使用过程,假定外在接口方法正常返回的情况下验证目标代码的正确性。常见的模拟工具有 EasyMock、JMock、Mockito。

单元测试最重要的是可重复性,不可重复性的单元测试是没有价值的。

单元测试和开发工作是同时进行的,有时候甚至是在开发工作之前。

  • 集成测试则是在功能开发代码开发完成之后,验证功能模块之间是否能工正常调用。

比如,当对 UserService 这个业务层类进行单元测试时,可以通过创建 UserDao 模拟对象,假设 DAO 层接口方法正常工作的情况下对 UserService 进行局部单元测试。

而对 UserService 进行集成测试时,则应该注入真实的 UserDao 对象进行。

所以一般来讲,集成测试面向的层面更高一些,一般对业务层和 Web 层进行集成测试;单元测试则面向一些功能单一的类,比如字符串格式化工具类、数据计算类等。

测试框架

Spring-Test 是 springframework 中的一个模块,是 Spring 提供的单元测试套件,可以方便的测试基于 Spring 框架的代码。

Spring-Test 是 Spring 框架的扩展,它专门用于帮助测试 Spring 应用,构建在 Junit 基础上,提供 Spring 容器相关的功能,比如测试 Spring Bean 的生命周期、Spring 配置加载等。

代码实践

1、引入依赖

xml 复制代码
<!-- 单元测试 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>5.2.10.RELEASE</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-core</artifactId>
	<version>3.11.2</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-inline</artifactId>
	<version>3.11.2</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.13.2</version>
	<scope>test</scope>
</dependency>

2、接口测试

java 复制代码
/**
 * Favor接口单元测试
 */
@RunWith(MockitoJUnitRunner.class)
public class FavorControllerTest {
    private MockMvc mockMvc;
    @Mock
    private FavorService favorService;
    @Mock
    private PersonService personService;
    @InjectMocks
    private FavorController favorController;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(favorController).build();
        // 模拟登录用户
        MockedStatic<SessionUtils> sessionUtils = mockStatic(SessionUtils.class);
        User mockUser = new User();
        mockUser.setId(1L);
        mockUser.setUserName("testUser");
        sessionUtils.when(SessionUtils::currentUser).thenReturn(mockUser);
    }

    /**
     * 测试请求报文是否合法
     *
     * @throws Exception
     */
    @Test
    public void listPage_ValidQuery_ShouldReturnFavorList() throws Exception {
        // 模拟查询结果
        List<Favor> favorList = new ArrayList<>();
        Favor favor = new Favor();
        favor.setId(1L);
        favor.setHoldPersonId(1L);
        favorList.add(favor);
        // 模拟业务层分页查询
        when(favorService.findPage(any(PageQuery.class))).thenReturn(favorList);
        // 模拟查询人员
        Person mockPerson = new Person();
        mockPerson.setId(1L);
        when(personService.findById(1L)).thenReturn(mockPerson);
        // 模拟Http请求
        String requestBody = "{\"terms\":{\"title\":\"someValue\"},\"page\":{\"pageNo\":1,\"pageSize\":10}}";
        mockMvc.perform(post("/favor/listPage")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk());
    }
}

为了行文简洁,忽略的 FavorController、FavorService 等部分代码的实现,不影响说明

注意:此代码是为了测试请求报文的合法性,即请求的响应码是否是 200,前提是接口在验证请求报文不合法时,会返回 200 以外的响应码。

不同的测试目标,代码也会不同。

3、DAO 层测试

java 复制代码
/**
 * DAO层测试
 */
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@Transactional
public class FavorItemDaoTest {

    @Autowired
    private FavorItemDao favorItemDao;

    @Before
    public void setUp() {
        // 如果需要,可以在这里进行任何通用设置
    }

    /**
     * 测试查询条件
     */
    @Test
    public void findByCondition_related() {
        Map<String, Object> map = ImmutableMap.of("receiveGiveRelated", true,
                "receivePersonIdList", Arrays.asList(1L, 2L, 3L, 4L),
                "givePersonIdList", Arrays.asList(11L, 12L));
        List<FavorItem> byCondition = favorItemDao.findByCondition(map);
        assertNotEquals(0, byCondition.size());
    }

}

这里的 Spring 配置文件建议尽量简洁,以加快测试方法执行。

相关推荐
小兔兔吃萝卜3 分钟前
Spring 创建 Bean 的 8 种主要方式
java·后端·spring
AAA修煤气灶刘哥1 小时前
面试官: SpringBoot自动配置的原理是什么?从启动到生效,一文讲透
后端·spring·面试
qq_三哥啊3 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis
别惹CC4 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj4 小时前
Spring事物
java·spring
IT毕设实战小研13 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋14 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员17 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
zru_960217 小时前
Spring Boot 单元测试:@SpyBean 使用教程
spring boot·单元测试·log4j
甄超锋18 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven