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 配置文件建议尽量简洁,以加快测试方法执行。

相关推荐
eggwyw4 分钟前
Spring 中使用Mybatis,超详细
spring·tomcat·mybatis
梵得儿SHI11 分钟前
SpringCloud 微服务实战:支付全链路生产级落地(接口对接 + 异步通知 + 订单状态闭环)
spring·spring cloud·微服务·微信支付·支付模块·支付集成·支付系统架构设计
爱吃烤鸡翅的酸菜鱼11 分钟前
Spring Cloud Eureka 服务注册与发现实战详解:从原理到高可用集群搭建
java·spring·spring cloud·eureka
sxhcwgcy32 分钟前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j
splage1 小时前
Spring Framework 中文官方文档
java·后端·spring
刘 大 望2 小时前
MCP详细介绍以及IDE和Spring AI中应用
java·ide·人工智能·spring·ai·aigc·ai编程
测试19983 小时前
单元测试、系统测试、集成测试的区别是什么?
自动化测试·软件测试·测试工具·单元测试·测试用例·集成测试·安全性测试
daidaidaiyu11 小时前
一文学习 Spring 声明式事务源码全流程总结
java·spring
彭于晏Yan16 小时前
Spring AI(二):入门使用
java·spring boot·spring·ai
卓怡学长17 小时前
m280本科生导师指导平台
java·数据库·spring·tomcat·maven·intellij-idea