当前位置: 首页 > news >正文

小架构step系列15:白盒集成测试

1 概述

白盒集成测试是spring-test提供的一种不需要启动Web Server就可以触发Controller里的http方法执行的一种方式,由于是从Controller就开始测试,相当于把对应方法后面的一整个流程都进行了测试,这个流程里面可能包含数据库的操作,可能包含文件操作,可能包含中间操作等,如果这些都需要进行配套,比如准备好文件存储的OSS、准备好类似kafka等消息中间件,那这个测试会相当难做,即使能够做也很难多做,成本非常高。本文就列一下这些场景的一种做法,仅供参考。

2 常用方式

2.1 数据库访问

2.1.1 内置数据库

开发一个业务系统,几乎都是需要数据库的,所以代码需要访问数据库,设计到这个场景的测试方法有两种。

一种是使用Spring提供的内置数据库,如HSQL、H2、Derby等,参考官方文档:https://docs.spring.io/spring-framework/docs/5.3.39/reference/html/data-access.html#jdbc-embedded-database-support

public class DataAccessIntegrationTestTemplate {private EmbeddedDatabase db;@BeforeEachpublic void setUp() {// creates an HSQL in-memory database populated from default scripts// classpath:schema.sql and classpath:data.sqldb = new EmbeddedDatabaseBuilder().generateUniqueName(true).addDefaultScripts().build();}@Testpublic void testDataAccess() {JdbcTemplate template = new JdbcTemplate(db);template.query( /* ... */ );}@AfterEachpublic void tearDown() {db.shutdown();}
}

 

此方式的好处是不需要安装数据库,也不需要清理数据,就像每个测试用例都用一个新的数据库一样。

2.1.2 外部数据库

第二种就是使用外部数据库,此时测试用例用上事务注解@Transactional,该注解由spring-tx包提供,需要引入spring-tx包的依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId>
</dependency>@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class HelloControllerTest {
}

从之前的原理来看,执行测试用例就相当于一个普通的方法调用,加上这个注解之后,相当于整个测试用例的执行都是在一个事务中执行,测试用例执行完成之后,测试框架会自动回滚,就相当于没有真正把数据写到数据库里,这样就不需要清理数据。

注意:此方式需要准备一个外部的数据库,并正常配置数据库连接参数。好处是真正能够测试到实际使用的数据库。

2.2 文件访问

当需要读取或者写出文件时,JDK提供了临时文件这种方式可以用到测试当中:

File tempFile = File.createTempFile("fileName", ".tmp");

在测试用例中生成一个临时文件,然后传到Service中代替业务实际使用的文件。

注意:要设计好如何把临时文件传到Service中,这可以倒逼把代码写得可测。

2.3 其它

其它类型,如使用消息中间件收发消息,使用ElasticSearch存取数据,使用OSS存取文件,使用HttpClient访问外部链接等,这些都需要封装出一个Repository或者Facade接口,通过mock的方式使用mock对象进行代替。

// 正常的业务代码
@Service
public class GroupMemberCreatorImpl implements GroupMemberCreator {private GroupMemberRepository repository;@Autowiredpublic GroupMemberCreatorImpl(GroupMemberRepository repository) {this.repository = repository;}@Overridepublic GroupMember create(Long groupId, String memberName) {GroupMember member = new GroupMember(groupId, memberName);return repository.save(member); // 在测试中,调此接口会触发mock对象的行为}
}// 测试代码
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {@Autowiredprivate MockMvc mockMvc;@MockBeanprivate GroupMemberRepository groupMemberRepository;@Testpublic void create_normal_group_member() throws Exception {Long groupId = 1L;String memberName = "zhangsan";GroupMember member = new GroupMember(groupId, memberName);// 当调Repository.save()时,返回值使用mock对象代替,不真正执行业务代码Repository.save()的逻辑Mockito.when(groupMemberRepository.save(Mockito.any())).thenReturn(member);MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/createGroupMember").contentType(MediaType.APPLICATION_FORM_URLENCODED).content("groupId=" + groupId + "&memberName=" + memberName)).andExpect(status().isOk()).andReturn();}
}
上面举的使用消息中间件收发消息,使用ElasticSearch存取数据,使用OSS存取文件,使用HttpClient访问外部链接等都是不太好测的代码,需要封装起来,封装的都是不带业务的技术操作,体现在尽量少的类中,以便能够排除掉,从而满足编写的代码可以达到100%的测试覆盖率。
比如使用消息中间件收发消息,消息的组装和拆解都属于业务操作,只有调消息中间件的SDK方法并传参和返回值属于技术操作,Repository或者Facade接口封装的是这类操作,这类操作主要是隔离一下,没有任何逻辑,所以也不用测试。
对于一个业务系统,这类场景还挺多的:
  • NoSQL数据库读写数据,如Redis、MongoDB等;
  • 消息中间件的收发消息,比如Kafka、RabbitMQ、RocketMQ等;
  • 搜索引擎搜索和存储数据,比如ElasticSearch;
  • 任务调度,如XXL-Job;
  • 图片/文件存取,如OSS;
  • 访问外部链接,如HttpClient;
  • 导入导出Excel数据,如EasyExcel、POI等;
  • 调用RPC接口;
  • 其它第三方SDK等;

3 架构一小步

1、对于数据库,准备一个有空表的数据库,使用@Transactional注解帮助测试;
2、涉及到文件的,则使用临时文件测试;
3、涉及到其它外部资源的(中间件、ElasticSearch等),把技术操作封装成接口,使用mock的方式测试,并测试配置中排除实际的实现类,使得测试覆盖率仍然可以100%;

文章转载自:
http://beautifully.zekgq.cn
http://aggrandizement.zekgq.cn
http://blagoveshchensk.zekgq.cn
http://absurd.zekgq.cn
http://banjo.zekgq.cn
http://arsenic.zekgq.cn
http://butterfish.zekgq.cn
http://bicorn.zekgq.cn
http://arraign.zekgq.cn
http://balpa.zekgq.cn
http://anxiolytic.zekgq.cn
http://alarmist.zekgq.cn
http://aviso.zekgq.cn
http://accrescence.zekgq.cn
http://anagrammatize.zekgq.cn
http://behead.zekgq.cn
http://astonied.zekgq.cn
http://assegai.zekgq.cn
http://cartridge.zekgq.cn
http://bolometer.zekgq.cn
http://agnosticism.zekgq.cn
http://biometeorology.zekgq.cn
http://aspca.zekgq.cn
http://chryseis.zekgq.cn
http://adrenalin.zekgq.cn
http://anteater.zekgq.cn
http://chit.zekgq.cn
http://assailant.zekgq.cn
http://chromophilia.zekgq.cn
http://arterialize.zekgq.cn
http://www.dtcms.com/a/280286.html

相关文章:

  • 大型语言模型(LLM)的技术面试题
  • 如何防止直线电机模组在高湿环境下生锈?
  • 《每日AI-人工智能-编程日报》--2025年7月15日
  • Volo-HTTP 0.4.0发布:正式支持 HTTP/2,客户端易用性大幅提升!
  • AI大模型训练的云原生实践:如何用Kubernetes指挥千卡集群?
  • Node.js 中http 和 http/2 是两个不同模块对比
  • Windows 安装 nvm-windows(Node.js 版本管理器)
  • 一键部署 Prometheus + Grafana + Alertmanager 教程(使用 Docker Compose)
  • sublime如何支持换行替换换行
  • HTTP性能优化实战技术
  • 一键直达人口分布数据
  • 606. 二叉树创建字符串
  • AutoGPT vs BabyAGI:自主任务执行框架对比与选型深度分析
  • Product Hunt 每日热榜 | 2025-07-15
  • 链表算法之【回文链表】
  • 药品挂网价、药品集采价格、药品上市价格一键查询!
  • 多租户SaaS系统中设计安全便捷的跨租户流程共享
  • PubSub is not defined
  • PyCharm 高效入门指南:从安装到效率倍增
  • Spark Expression codegen
  • 用TensorFlow进行逻辑回归(六)
  • Spark 之 Join BoundCondition
  • windows内核研究(进程与线程-KPCR)
  • C++题解(37) 信息学奥赛一本通1318:【例5.3】自然数的拆分
  • 【GEOS-Chem模型第一期】模型概述及网页总结
  • 网络基础10--ACL与包过滤
  • C++11:constexpr 编译期性质
  • MySQL 备份与恢复指南
  • 【PTA数据结构 | C语言版】二叉树层序序列化
  • 【UV环境】使用uv快速创建环境