前言
在软件开发流程中,单测是非常重要的一环,保证代码质量和效率。但是,实际开发过程中很少有同学会花时间去写单测。一方面,由于项目节奏等原因没时间去写单测代码,另一方面,单测框架太多,找到一个好用其效率高的单测工具也需要一定的成本。
然而,单测除了能验证本身代码的逻辑外,还能对一些特殊场景进行验证,保证代码的健壮性,不会因为日后的需求迭代,结构变更而导致或者新增风险。所以,单测的好处很明显,需要大家在单测和代码直接做一个平衡。
单测的概念
先看一个概念,什么是单元测试:
单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
所以,单元测试主要包括以下几点:
- 测试范围:单元测试测的是一个单元内部的逻辑,而不是各个模块之间的交互,模块之间的交互应该被称作集成测试。
- 环境依赖:单元测试不应该依赖运行环境,哪怕整体环境无法在跑起来也可以进行测试。
- 测试效率:单元测试的效率应该足够高,能方便、快速的运行测试代码。
单测技术介绍
|通常单测分为三个部分
Runner:Runner的目的是为了帮助我们做资源加载等事情,定制单元测试的模板,让我们能够专注于单测case本身的书写,Runner可以理解为单元测试的基础框架。
常用的Runner:junit4,TestNG,IntlTest(IntlTestBlockJUnit4ClassRunner),springTest & springbootTest & SpringContainer4Test(SpringJunit4ClassRunner)等
Mocks:代理被测试对象,真正对被测试的代码进改造,屏蔽其他外部依赖,让我们的测试用例能够调到我们的测试代码而不是真正的代码。
常用的Mock工具:Mockito,此外还有JMockit、EasyMock、jMock、PowerMock等。
Mock常用的场景:
- 外部依赖的应用的调用,比如WebService等服务依赖。
- DAO层(访问MySQL、Oracle、Emcache等底层存储)的调用等。
- 系统间异步交互通知消息。
- methodA里面调用到的methodB。
- 一些应用里面自己的Class(abstract,final,static)、Interface、Annotation、Enum和Native等。
对测试的结果进行断言判断。
常用的断言工具:AssertJ,JSONassert
单测技术选型
1、Runner选择目前比较流行的测试框架主要有两种:Junit4和TestNG,对比如下:
主要关注的差别点:
【1】参数化测试
参数化测试是指给单元测试传多种参数值,验证接口对多种不同参数的处理是否正确。
对于n个不同参数组合的测试,JUnit 4要写n个测试用例。每个测试用例完成的任务基本是相同的,只是受测方法的参数有所改变。TestNG的参数化测试只需要一个测试用例,然后把所需要的参数加到TestNG的xml配置文件中。这样的好处是参数与测试代码分离,非程序员也可以修改参数,同时修改无需重新编译测试代码。
JUnit4的使用限制比TestNG多,并且TestNG使用更加灵活。
Junit4 :
测试注解:@RunWith、@Parameter
问题:测试每一个方法都需要新建一个类,并且得按照下面Demo中的方式传参:写一个@Parameters方法返回List的参数,并通过构造函数的方式进行传参。
@RunWith(value = Parameterized.class)
public class JunitTest6 {
private int number;
public JunitTest6(int number) {
this.number = number;
}
@Parameters
public static Collection<Object[]> data() {
Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };
return Arrays.asList(data);
}
@Test
public void pushTest() {
System.out.println("Parameterized Number is : " number);
}
}
TestNG
用XML文件或者注解的方式进行传参:
XML文件:(只支持基础数据类型)
public class TestNGTest6_1_0 {
@Test
@Parameters(value="number")
public void parameterIntTest(int number) {
System.out.println("Parameterized Number is : " number);
}
}
参数化测试(支持复杂数据类型)
@Test(dataprovider = "Data-Provider-Function")
public void parameterIntTest(Class clzz, String[] number) {
System.out.println("Parameterized Number is : " number[0]);
System.out.println("Parameterized Number is : " number[1]);
}
//This function will provide the patameter data
@DataProvider(name = "Data-Provider-Function")
public Object[][] parameterIntTestProvider() {
return new Object[][]{
{Vector.class, new String[]{"java.util.AbstractList", "java.util.AbstractCollection"}},
{String.class, new String[] {"1", "2"}},
{Integer.class, new String[] {"1", "2"}}
};
}
【2】依赖测试
JUnit 4测试的依赖性非常强,测试用例间有严格的先后顺序。前一个测试不成功,后续所有的依赖测试都会失败。TestNG 利用@Test 的dependsOnMethods属性来应对测试依赖性问题。某方法依赖的方法失败,它将被跳过,而不是标记为失败。
TestNG在依赖测试场景上更加的灵活
@Test
public void method1() {
System.out.println("This is method 1");
}
@Test(dependsOnMethods={"method1"})
public void method2() {
System.out.println("This is method 2");
}
【3】其他差别
1. JUnit只能进行单元测试,TestNG可以进行单元测试,功能测试,端到端测试,集成测试等。
2. TestNG需要一个额外的xml配置文件,配置测试的class、method甚至package。
3. TestNG的运行方式更加灵活:命令行、ant和IDE,JUnit只能使用IDE。
4. TestNG的annotation更加丰富,比如@ExpectedExceptions、@DataProvider等。
5. 测试套件运行失败,JUnit 4会重新运行整个测试套件。TestNG运行失败时,会创建一个XML文件说明失败的
综上:Runner的选型,我们选用TestNG。
Mocks的选择Mock是我们代码中使用最多的一种操作了,Mock工具的好坏直接影响了我们单测的效率。
Mock工具有很多,常见的有JMockit、EasyMock、jMock、PowerMock等,几种Mock工具的原理大同小异
|Jmockit
中文网网址:http://jmockit.cn/
(推荐)jmockit Tutorial :https://zhuanlan.zhihu.com/p/24719968
Assert选择assert工具在这里推荐使用AssertJ。
选择AssertJ的原因:
- 流式断言,代码即用例,直观易懂
举个例子:
判断一个字符串包不包括a跟b两个字符。要这么写
//junit或testng
assertTrue(stringbuffer.contains("a") && stringbuffer.contains("b"))
//AssertJ
assertThat(stringbuffer).contains("a").contains("b").as("判断字符串是否包括a|b")
相比之下,显然后者更加容易理解,而且as的注释更是让断言清晰。
- 强大的API支持
- api库更强大。除了以上基础类型和异常、日期、类属性、soft断言api,更突出的优势是扩展了对以下领域的支持:DB(据说适配myBatis, Hibernate, JOOQ等多种DB框架)、Guava、Swing;Uri、xml、file;jdb8如:Future,Stream, Optional, Java 8 Date等
- api可读性更好,更加贴近自然语义,AssertJ中封装了海量的api,基本都可以从名字中明确理解含义
- 可读性更好
- 可自定义断言器
具体可看AssertJ官网地址:http://joel-costigliola.github.io/assertj/
,