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

单元测试的最佳实践

整体架构

891538a3dc589da6d123e79d1f3448a7.jpeg

合适的架构可以提升可测试性。比如菱形对称架构的模块化和解耦特性使得系统各个部分可以独立进行单元测试。这不仅提高了测试的效率,还能够减少测试的依赖性,提高测试准确性。

代码设计

代码设计和可测试性有密切关联。强烈建议一个方法的代码行数不要太多。这样,如果需要细粒度单元测试(需要比较高的代码覆盖率的单元测试),就更容易。大不了把私有方法变成公有,加一

@VisibleForTesting注解来测试。

其他设计比如采用易于测试的设计模式:‌选择合适的设计模式可以显著提高代码的可测试性。‌例如,‌使用依赖注入(‌DI)‌模式可以使依赖关系在运行时动态绑定,‌便于模拟和替换依赖项,‌从而更容易进行单元测试。‌

测试框架

分布式微服务系统而言,“粗粒度单元测试”最大挑战在于如何解决周边依赖问题,对于一个典型的应用而言,它可能会依赖数据库、消息中间件、缓存系统等,以及周边的其它服务。如何处理这些依赖,是我们必须要面对和要解决的问题。

其实解决方案只有一种叫Test Double(测试替身),即要用“替身”来代替依赖,从而让程序在没有真正依赖环境的情况下,仍然可以运行,验证功能。这些“替身”有不少fancy的名字,比如dummy、fake、stub、mock、spy等,这里我就不啰嗦这些概念的差异了,也不重要。重要的是我们该如何高效的实施“粗粒度单元测试”。

Mock做替身

使用mock做“替身”是我们处理外部依赖的最常见做法,有很多的框架比如Mockito,EasyMock支持我们去做mock的事情。

这种方法有效,但存在两个问题:

比较繁琐,当需要构造的对象有大量字段的时候,需要额外写很多的代码。

有些功能测不全,比如这里的SQL语句就不能被测试到,因为数据库的访问是被mock掉的。

Embedded Server做替身

鉴于以上的问题,我发现很多团队都开始转向embedded方案,即在本地拉起一个真实的内嵌依赖服务,作为依赖的“替身”。比如我用到了Redis缓存,那么我可以使用embedded-redis启动一个本地的redis,从而连接到“真实”的redis环境。使用很简单,只需要加入下面的依赖就可以。在Junit5的Extension帮助下,我们可以很优雅的使用embedded的服务。

<dependency><groupId>com.github.codemonstur</groupId><artifactId>embedded-redis</artifactId><version>1.4.2</version><scope>test</scope>
</dependency>

类似的,我们常用的中间件比如kafka、MySQL、MongoDB等等都有相对应的embedded的方案。对于非中间件,比如其它服务依赖,我们可以使用wiremock来做其它服务API的“替身”。这些动作在很大程度上可以帮助我们消解依赖问题,这样做可以给我们带来以下好处:

在同样代码覆盖率的情况下,使用embedded的集成测试方案,可以写更少的测试用例和代码,研发效率更高

因为使用的是真实中间件服务,可以让我们的测试更加贴近真实环境,测试的有效性更高。

测试的入口是接口。这种更大测试粒度带来的好处是,可以帮助我们更好的重构内部业务逻辑,而不用担心破坏测试用例,如果UT太细的话,重构代码的同时还要重写UT,比较麻烦。

你也许会说,和Mock方案相比,这里的数据准备并没有比Mock少多少啊,实际上的确如此,对于测试数据,我们可以使用测试夹具(Test Fixtures)把测试数据用json/xml的形式放在resource下面,这样可以提升我们的数据复用和数据准备效率。

除此之外,embedded方案还引入了一个比较严重的问题——慢!即在运行测试的时候,需要拉起整个环境,整个过程很耗时,一般情况下,如果要启动3个embedded服务,短则一分钟多则几分钟,这显然与我们所期望的单测要“短平快”不相符。“慢”会极大的抑制我们运行UT的积极性,也就不能享受UT带来的quick feedback好处,更不用说TDD了。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • UDP/TCP协议解析
  • Windows 下的sqlserver数据拷贝到linux
  • Cadence23学习笔记(十四)
  • 深入浅出C语言指针(进阶篇)
  • 自动化测试--WebDriver API
  • element表单disabled功能失效问题
  • eqmx上读取数据处理以后添加到数据库中
  • 华为Ascend C算子开发(中级)考试
  • web网站组成
  • 《华为数据之道》读书笔记六---面向自助消费的数据服务建设
  • powershell自定义命令别名
  • pglogical扩展的基本用法介绍
  • leetcode日记(51)不同路径Ⅱ
  • 数据治理之“财务一张表”
  • python-爬虫实例(1):获取京东商品评论
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【译】理解JavaScript:new 关键字
  • ➹使用webpack配置多页面应用(MPA)
  • Angular4 模板式表单用法以及验证
  • Facebook AccountKit 接入的坑点
  • orm2 中文文档 3.1 模型属性
  • Python 反序列化安全问题(二)
  • Python打包系统简单入门
  • redis学习笔记(三):列表、集合、有序集合
  • 测试如何在敏捷团队中工作?
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 聊聊flink的BlobWriter
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 网页视频流m3u8/ts视频下载
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (C语言)fread与fwrite详解
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (苍穹外卖)day03菜品管理
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (离散数学)逻辑连接词
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • .Net Core 微服务之Consul(二)-集群搭建
  • .net core控制台应用程序初识
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .net6+aspose.words导出word并转pdf
  • .NET8 动态添加定时任务(CRON Expression, Whatever)
  • .NET命令行(CLI)常用命令
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [<事务专题>]
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [AWS]CodeCommit的创建与使用
  • [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)