如何定义单元

对于单元测试中的单元,不同的人有不同的看法:可以理解为一个方法,可以理解为一个完整的接口实现,也可以理解为一个完整的功能模块或者是多个功能模块的一个耦合。

根据以往的单元测试经验,在设计单元测试用例时,当针对方法级别展开单元测试时,重点关注的是方法的底层逻辑;当针对的是模块时,针对的是实际的业务逻辑实现;当针对整合后的模块进行测试时,一般称之为集成测试。

不管是单元测试还是集成测试,都可以统一的理解为单元测试。因为他们的本质都是对方法或接口的一种测试形式,只是所处的阶段不一样罢了。

1. 集成测试应该由谁编写

在我们的实际工作中,研发人员在提交代码之前,会设计一些“冒烟测试”级别集成测试用例。等到整个功能开发完成后,测试人员会根据业务需求和设计的测试用例,来进行整体的集成测试用例的编写、执行、失败用例分析,以及代码的调式和问题代码的定位等工作。

2. 集成测试用例

业务相关的测试主要是通过spring-test来进行集成测试,基本的测试结构为先定义一个基类用来初始化被测试类。

测试基类定义结构如下:

@RunWith(SpringJUnit4ClassRunner.class) 
ContextConfiguration(locations = {"classpath:./spring/applicationContext.xml"}) 
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) 
public class BaseSpringJunitTest { 
@Autowired 
protected BusinessRelatedServiceImpl businessRelatedService; 
}

业务相关的测试类定义如下格式:

public class BusinessRelatedServiceImplDomainTest extends BaseSpringJunitTest { 
@Test 
public void testScenario1 (){ 
new Thread(new DOSAutoTest("testScenario1")).start(); 
Thread.sleep(1000*60*1); 
String requestJson=""//测试入参; 
RequestPojo request=( RequestPojo )JSONUtils.jsonToBean(requestJson,RequestPojo .class); 
ResponsePojo response= businessRelatedService.businessRelatedMethod(ResponsePojo ); 
//业务相关的assert区域 

}

3. 如何解决下游系统依赖

businessRelatedMethod方法在处理业务逻辑的过程中需要调用下游JSF(Jingdong Service Framework,完全自主研发的高性能RPC服务框架)提供的订单接口(OrderverExportService),并根据入参中的订单编号获取订单的详细信息(ResultPojo getOrderInfoById (long orderId))。

那么如何获取下游JSF接口的返回正确数据就变成了一个比较重要的问题。如果是在功能测试或者联调测试阶段,可以由下游测试人员来提供数据。不过这样沟通和测试成本较高,无法满足业务快速上线和变化的要求,尤其在集成测试阶段这个问题就变得尤为明显,因为下游数据对于上游来说是不可控的。这样mock下游数据就变得尤为紧急和重要。

4. Mock框架的选择

在整个java生态圈中,支持mock的开源框架还是比较多的,比如常用的mockito、powermock、easymock和jmockit等开源框架。这些框架在mock方面都具有比较强大的功能与比较广泛的使用量。但是这些框架都具有一个相同的缺点,那就是需要或多或少的编码工作来mock所需要的接口返回数据。

在设计mock框架的时候,我们考虑到尽量让写单元测试的人员或研发人员少编码或不编码,来获取不同的业务场景所需要的测试数据。

Mock框架 第一版

该版本的mock框架的整体思想为:结合JSF的特性,Override所有下游接口的方法,然后将实现下游接口的应用部署到测试环境,发布一个有别与真实下游接口的服务,在接口调用的时候,通过不同的JSF接口别名来进行区分。Mock的数据存储在数据库中。

该框架类调用关系:

Mock接口的具体实现:

public class OrderverExportServiceImp extends OrderverExportServiceAdapter { 
@Resource 
private OrderverMapper orderverMapper;

@Override 
public ResultPojo getOrderInfoById (long orderId) { 
OrderverPojo orderverMock=orderverMapper.getOrderId(new Long(orderId).toString()); 
ResultPojo result=new ResultPojo (); 
result.setFiled1(null); 
result.setFiled1(0); 
result.setFiled2(null); 
result.setFiled3(null); 
result.setResult(true); 
…//mock需要的数据 
result.setReturnObject(orderver); 
return result; 

}

Mock服务发布完后的效果:

在集成测试阶段,只需要修改该接口的JSF别名,就可以实现该接口的mock调用。

<jsf:consumer id="orderverExportServiceJsf" interface="xxx.xxx.xxx.xxx.xxx.OrderverExportService" 
protocol="jsf" timeout="${timeout}" 
alias="${alias}" retries="2" serialization="hessian"> 
</jsf:consumer> 
alias=orderver_mock

该框架的优缺点

优点:

做集成测试用例设计时,不用编写代码,只需要维护测试场景所需要的返回数据;

该框架不仅可以用在集成测试中,在下游接口无变更的前提下,同时还可以用在后续系统测试与联调测试阶段。

缺点:

mock服务的发布依赖于服务器与数据库,当依赖的服务器或数据库出现跌机情况时,该mock服务不用;

该框架的维护成本比较大,当下游依赖的接口较多时,所有的服务包含的方法均需要进行override;

当下游的接口定义发生变化时比如新增接口方法,该mock服务需要重新override该新增的方法并且需要重新打包部署;

下游接口方法的数据结构发生变化时,存储数据的数据表结构需要做相应的调整,对于业务变化较快的系统,这种类型的改动频率还是较高。

Mock框架 第二版

为了解决上述mock框架依赖服务器与数据库的问题,我们又做了第二次尝试。将mock框架设计为jar包的形式,提供给程序来调用。在下游接口的实现方式上第二版与第一版保持不变,同时业务数据不放数据库,而是将业务数据放到文件中。变化的点为接口调用上需要将对应的jsf:comsumer节点替换为对应的实际mock的实现类。

Mock接口的实现:

@Service("orderverExportService") 
public class OrderverExportServiceMock extends OrderverExportServiceAdapter { 
@Override 
public ResultPojo getOrderInfoById(long orderId) { 
ResultPojo result=new ResultPojo (); 
result.setFiled1(null); 
result.setFiled1(0); 
result.setFiled2(null); 
result.setFiled3(null); 
result.setResult(true); 
…//mock需要的数据 
result.setReturnObject(orderver); 
return result; 

}

Mock接口调用配置:

<!--<jsf:consumer id="orderverExportServiceJsf" interface="xxx.xxx.xxx.xxx.xxx.OrderverExportService" protocol="jsf" timeout="${timeout}"alias="${alias}" retries="2" serialization="hessian"> 
</jsf:consumer>--> 
< bean id="orderverExportServiceJsf" class="xxx.xxx.xxx.xxx.xxx.OrderverExportServiceMock"></bean>

该框架的优缺点

优点:

做集成测试用例设计时,不用编写代码,只需要维护测试场景所需要的返回数据;

相比较第一个版本,该版本在执行效率上有了较大的提升,因为mock类的加载是走的本地Spring配置文件,同时数据加载也是走的本地文件;

无需再依赖于服务器部署和数据库依赖。

缺点:

该框架的维护成本比较大,当下游依赖的接口较多时,所有的服务包含的方法均需要进行override;

当下游的接口定义发生变化时比如新增接口方法,该mock服务需要重新override该新增的方法并且需要重新打包,然后上传到maven仓库;

下游接口方法的数据结构发生变化时,对于业务变化较快的系统,这种类型的改动频率还是较高。

Mock框架 第三版

随着需要mock的接口变的越来越庞大,以上两种mock框架的实现的缺点就变的越来越突出。该框架可以说从根本上解决了上述框架实现的问题。因为该框架充分利用了JDK的动态代理,反射机制以及JSF提供的高级特性来实现我们的mock框架。框架维护任务可以做到无需做更多的针对接口的编码任务。测试人员只需要将重点放在测试数据的准备上。

框架整体调用时序图:

框架的核心类图:

其中DOSAutoTest类用来启动和发布JSF的mock接口,JSFMock通过动态代理的方式,实现下游接口的mock功能并根据测试场景获取对应的mock数据。

其中,mock的数据以json格式存储在mock框架项目工程的指定目录下。

该框架解决的问题:

省去了利用第三方mock框架如jmockit,mockito,powermock时,需要在单元测试或集成测试类中写mock代码的麻烦;

该框架模拟数据返回时,完全的模拟了接口之间的调用关系;

测试人员或研发人员在利用该框架mock数据时,无需额外的代码,就可以实现mock数据的返回;

在模拟下游数据返回时,发布的mock接口调用完成后就自行销毁,无需额外服务器进行部署与维护。

在进行接口mock时,无需在mock框架中添加相关的接口maven依赖。

单元测试展开方式

1. 单元测试应该由谁编写

单元测试由谁编写?针对这个问题,大家在网上会找到不同的观点:

一个观点是,谁写代码,谁自己写单元测试。当然,有的结对编程里面,也有相互写的,不过,这个过程中,两个人是共同完成的代码。也不违反谁写代码谁写单元测试的原则。

另一个观点是单元测试应该由其它的研发人员或测试人员来进行编写,理由大概可以理解为对于非代码编写人员来说,在设计单元测试用例的时候,对应的是一个黑盒。在这样的背景下,设计出来的用例覆盖程度更高。

2. 单元测试的行业现状

如果研发来负责单元测试的编写,很多时候研发人员都不编写单元测试。研发人员不编写单元测试的原因其实也是比较容易理解的,因为编写单元测试用例工作太耗时。有时候研发的经理或项目的业务方会认为单元测试用例会减缓项目的整体进度。有时候甚至整个公司层面都不认可花费大量的时间在单元测试上是合理的,尤其是在项目周期紧张和业务变动较大的项目上。因为单元测试从一定程度上来说确实增加的研发人员的编码量,同时还会增加代码的维护成本。

如果测试来负责单元测试的编写,目前的现状是测试人员需要时间理解代码,写单元测试的时间会变长。有代码修改之后,在项目的测试压力之下,有的测试人员,就选择不维护单元测试,而选择赶紧完成传统的手工测试。

3. 单元测试用例自动生成

人工编写测试用例成本增加,那么我们考虑是否可以通过自动生成的方式来实现单元测试呢?EvoSuite是由Sheffield等大学联合开发的一种开源工具,用于自动生成测试用例集,生成的测试用例均符合Junit的标准,可直接在Junit中运行。

对于非业务相关的模块,在单元测试的实践中,就可以直接使用上述工具来自动生成单元测试代码。虽然该工具只是辅助测试,并不能完全取代人工,测试用例的正确与否还需人工判断,但是通过使用此自动测试工具能够在保证代码覆盖率的前提下极大地提高测试人员的开发效率。

下面来详细介绍如何使用该工具生成单元测试用例以及如何检查单元用例的正确性。

EvoSuite为Maven项目提供了一个插件,该插件的具体配置如下所示:

<plugin> 
<groupId>org.evosuite.plugins</groupId> 
<artifactId>evosuite-maven-plugin</artifactId> 
<version> ${evosuiteVersion} </version> 
<executions><execution> 
<goals> 
<goal> 
prepare 
</goal> 
</goals> 
<phase> 
process-test-classes 
</phase> 
</execution></executions> 
</plugin>

除了需要配置上述plugin外,maven还需要做如下的配置:

<dependency> 
<groupId>org.evosuite</groupId> 
<artifactId>evosuite-standalone-runtime</artifactId> 
<version>${evosuiteVersion}</version> 
<scope>test</scope> 
</dependency> 
<!--上述依赖主要是用来自动生成单元测试用例--> 
<plugin> 
<groupId>org.apache.maven.plugins</groupId> 
<artifactId>maven-surefire-plugin</artifactId> 
<version>${maven-surefire-plugin-version}</version> 
<configuration> 
<systemPropertyVariables> 
<java.awt.headless>true</java.awt.headless> 
</systemPropertyVariables> 
<testFailureIgnore>true</testFailureIgnore> 
<skipTests>false</skipTests> 
<properties> 
<property> 
<name>listener</name> 
<value>org.evosuite.runtime.InitializingListener</value> 
</property> 
</properties> 
</configuration> 
</plugin>

上述plugin主要是用来混合执行手动设计的单元测试用例和使用EvoSuite自动生成的单元测试用例。

以上EvoSuite所需的plugin和maven依赖配置完成之后,就可以使用maven命令来自动生成单元测试用例并执行了。

mvn -DmemoryInMB=2000 -Dcores=2 evosuite:generate evosuite:export test

生成测试用例后,可以通过人工排查生成测试用例的正确性。

写在最后

不管是研发还是测试负责集成或单元测试,选取适合自身项目的mock框架,一方面可以缩短测试代码的编写时间,另一方面可以加速测试代码的执行效率,同时又可以降低测试代码的维护成本。不管是行业中通用的mock框架还是定制化的框架,都可以广泛的应用的测试中。

因为做mock框架不是目的,目的是为了能高效的设计出更多的测试覆盖场景,来进一步提升测试效率、保证产品质量和将测试人员从繁重的手工测试中得以解放。

跟大家推荐一个学习资料分享群:747981058,里面大牛已经为我们整理好了许多的学习资料,有自动化,接口,性能等等的学习资料!人生是一个逆水行舟的过程,不进则退,咱们一起加油吧!

Mock框架的三次迭代,让你的单元测试更高效相关推荐

  1. 迭代规划会怎么开才能更高效?

    上一期内容中,我们为大家介绍了摸鱼小技巧.今天,我们来了解一下敏捷开发过程中每个迭代都会召开的「迭代规划会」到底应该怎么开,以及如何借助猪齿鱼高效.便捷的开展迭代规划会,一起来看看吧! 阅读本文你将学 ...

  2. Mock 框架 Moq 的使用

    Mock 框架 Moq 的使用 Intro Moq 是 .NET 中一个很流行的 Mock 框架,使用 Mock 框架我们可以只针对我们关注的代码进行测试,对于依赖项使用 Mock 对象配置预期的依赖 ...

  3. 单元测试mock框架——jmockit实战

    JMockit是google code上面的一个java单元测试mock项目,她很方便地让你对单元测试中的final类,静态方法,构造方法进行mock,功能强大.项目地址在:http://jmocki ...

  4. Java单元测试之Mock框架

    一.引言 二.为什么要用Mock 三.Mock使用场景 四.Mock定义 五.Mock框架 五.Mockito 5.1 Mockito基本使用 5.2 MockMVC测试 5.2.1 初始化MockM ...

  5. Android 开发之手把手教你写 ButterKnife 框架(三)

    系列文章目录导读: Android开发之手把手教你写ButterKnife框架(一) Android开发之手把手教你写ButterKnife框架(二) Android开发之手把手教你写ButterKn ...

  6. 【C#】【xUnit】【Moq】.NET单元测试Mock框架Moq初探!

    在TDD开发模型中,经常是在编码的同时进行单元测试的编写,由于现代软件开发不可能是一个人完成的工作,所以在定义好接口的时候我们就可以进行自己功能的开发(接口不能经常变更),而我们调用他人的功能时只需要 ...

  7. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

  8. pytest测试框架_聊聊 Python 的单元测试框架(三):最火的 pytest

    本文首发于 HelloGitHub 公众号,并发表于 Prodesire 博客. 一.介绍 本篇文章是<聊聊 Python 的单元测试框架>的第三篇,前两篇分别介绍了标准库 unittes ...

  9. 【运筹学】线性规划 人工变量法 ( 人工变量法案例 | 第三次迭代 | 中心元变换 | 检验数计算 | 最优解判定 )

    文章目录 一.第三次迭代 : 中心元变换 二.第三次迭代 : 单纯形表 三.第三次迭代 : 检验数计算 四.第三次迭代 : 最优解判定 五.第三次迭代 : 最终单纯形表 上一篇博客 [运筹学]线性规划 ...

最新文章

  1. linux下 proc 目录
  2. Linux平台Qt creator报错:Circular all - first dependency dropped
  3. OS X 要改名为 MacOS 是因为 iOS 10 要来了?
  4. JavaScript-回调函数
  5. 160-PHP 文本替换函数str_replace(一)
  6. 一文彻底搞懂静态库和动态库,显示链接和隐式链接
  7. Nodejs 安装 on centos7
  8. mybatis mysql schema_学习Mybatis与mysql数据库的示例笔记
  9. hitool备份3798固件方法_创维E900s海思3798芯片当贝桌面不拆机通刷固件及刷机教程201910版...
  10. petshop 数据库
  11. 《MySQL技术内幕(SQL编程)》——查询处理
  12. 【iCore4 双核心板_ARM】例程三:EXTI中断输入实验——读取ARM按键状态
  13. 【中间件技术】第二部分 CORBA规范与中间件(3) 基于CORBA的开发过程
  14. 世界一流学科排名计算机科学,2021软科世界一流学科排名:计算机科学与工程...
  15. git冲突解决和放弃本地操作
  16. Credential Harvester的脚本修改
  17. win10 windows许可证即将过期的解决办法
  18. 扫雷c语言完整源代码,C语言扫雷源码
  19. 计算机课有平时成绩吗,大学计算机基础课程平时成绩评定方法探究.doc
  20. 三菱FX系列PLC以太网连接西门子wincc软件

热门文章

  1. 激活函数之ReLU函数
  2. fdk-aac 编译配置
  3. OI 守望者的逃离 c语言简洁注释 逻辑清晰
  4. 专业游戏录屏软 Camtasia 2023强悍来袭,Camtasia 2023软件安装激活教程
  5. P1095 守望者的逃离
  6. Pytorch学习笔记04----梯度下降算法
  7. scrapy爬取需要登录的网站(知乎)
  8. 2017年你不得不知的学习诀窍
  9. 21计算机保研经验分享
  10. Android4.2距离感应器