在计算机编程中,单元测试1(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确检验的测试工作。对于面向对象编程,最小单元就是方法,包括基类、抽象类、派生类中的方法。
通常来说,每修改一次代码就要进行最少一次单元测试,在编写过程中可能要进行多次单元测试,以证实程序达到软件规格书要求的工作目标,没有程序错误。
每个理想的测试案例独立于其它案例;为测试时隔离模块,经常使用stubs、mock、fake等测试马甲程序。单元测试经常有开发者编写,用于确保他们所写的代码匹配软件需求和遵循开发需求。它的实施方式可以是非常手动的,或者是构建自动化的一部分。
(0)技术选型
单元测试主要涉及到测试框架部分:
a)TestNG(Next Generation Java Testing)2
简介:是一个测试框架,其灵感来自于JUnit和NUnit,但引入了一些新功能,使其功能更强大
testng注解一览:
注解格式 | 说明 |
---|---|
@BeforeSuite | 被注释的方法将在所有测试运行前运行 |
@AfterSuite | 被注释的方法将在所有测试运行后运行 |
@BeforeTest | 被注释的方法将在测试运行前运行 |
@AfterTest | 被注释的方法将在测试运行后运行 |
@BeforeGroups | 被配置的方法将在列表中的gourp前运行。这个方法保证在第一个属于这些组的测试方法调用前立即执行。 |
@AfterGroups | 被配置的方法将在列表中的gourp后运行。这个方法保证在最后一个属于这些组的测试方法调用后立即执行。 |
@BeforeClass | 被注释的方法将在当前类的第一个测试方法调用前运行。 |
@AfterClass | 被注释的方法将在当前类的所有测试方法调用后运行。 |
@BeforeMethod | 被注释的方法将在每一个测试方法调用前运行。 |
@AfterMethod | 被注释的方法将在每一个测试方法调用后运行。 |
b)Mockito3
简介:java开发测试模拟框架。是一个针对于java的mocking框架。模拟和校验模拟结果
(1)建立测试类
正式开始编码之前请在相应开发工具中 下载TestNG插件 ,方便测试 |
---|
/*
所有的测试方法的都要加上@Test注解并且只要是测试方法就不能够有参数
@Test注解的使用:
1.引入包import org.testng.annotations.Test;
2.当有多个测试方法时候,执行的顺序默认是按照字典序升序执行,
也可以使用@Test(priority=1)的方式指定执行顺序。数值越小越先被执行
3.当有多个测试方法时候,有部分测试方法还没有完成编写,可以使用
@Test(enable=false),这样在运行时候,就不会执行该测试方法
*/
package com.xxx.app.appprocess;
import org.testng.annotations.Test;
public class AccessProcessImplTest
{
//声明变量对象...
//测试方法列
@Test
public void Xxx1Test()//测试方法的命名要注意符合规范 原来的方法名+场景+Test
{
......
}
@Test
public void Xxx2Test()
{
......
}
}
<suite name="Suite" parallel="classes" thread-count="1">
<test name="MQTest">
<classes>
<class name=""/>
classes>
test>
<listeners>
<listener class-name="org.uncommons.reportng.HTMLReporter" />
<listener class-name="org.uncommons.reportng.JUnitXMLReporter" />
listeners>
suite>
运行,该测试类上 右键run as TestNG test
测试结果:
其中‘ run: 1, Failures: 0 ’表示,1个成功0个失败
[TestNG] Running:
C:\Users\Mr\AppData\Local\Temp\testng-eclipse--676935048\testng-customsuite.xml
hello TestNg
PASSED: XxxTest
===============================================
Default XxxTest
Tests run: 1, Failures: 0, Skips: 0
===============================================
===============================================
Default suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
[TestNG] Time taken by org.testng.reporters.SuiteHTMLReporter@45e35a2f: 53 ms
[TestNG] Time taken by [TestListenerAdapter] Passed:0 Failed:0 Skipped:0]: 1 ms
[TestNG] Time taken by org.testng.reporters.EmailableReporter@70ab4338: 5 ms
[TestNG] Time taken by org.testng.reporters.XMLReporter@256ee01c: 14 ms
[TestNG] Time taken by org.testng.reporters.JUnitReportReporter@8ba0dd4: 6 ms
(3)Mockito在单元测试中的使用
一个问题
请大家思考下这个问题:如果一个单元测试中涉及到数据库的操作、文件读写、网络开销、接口操作等等一系列耗时或者实现可能性小的操作,这种情况下我们该如何处理这些数据。
分析:本来单元测试就是一个个独立模块的测试,只测试代码的业务逻辑是否行的通,一旦单元测试牵扯到相互依赖的类,就要使用模拟类把需要用到的第三方对象模拟出来,这就是我理解的Mock的意义。
我们来看下来自引用博客的理解:
什么是Mock测试
Mock测试是单元测试的重要方法之一。Mock测试就是在测试过程中,对于某些不易构造的对象(如HttpServletRequest必须在Servlet容器中才能构造出来),或者不易获取比较复杂的对象(如JDBC中的ResultSet对象),用一个虚拟的对象(Mock对象)来创建,以便测试的一种测试方法。
Mock最大的功能就是帮你把单元测试的耦合分开,如果你的代码对于另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为,且默认这些依赖都是正确的,来测试你的代码是否正确。
比如,存在以下一段代码依赖,当我们需要测试A类的时候,如果没有Mock,我们就需要把整个依赖树都构建出来:
而当我们用Mock时候,就可以将结构分解开来:
Mock对象的使用范畴
真实对象具有不可确定的行为,产生不可预测的结果(如:股票行情、天气预报);
真实对象很难被创建;
真实对象的某些行为很难被触发 ;
真实对象实际上还不存在的等等
Mockito和Mock的关系
Mockito是java单元测试的开源的Mock框架。Mockito拥有非常少的API,所以开始使用Mockito时候,很便捷,因为只有一种创建mock的方式。只要记住在执行前打桩、而后在交互中验证。
特点:
可以mock具体的类,而不单单是接口;
注解语法糖–@Mock;
失败验证的堆栈跟踪完整、简洁;
允许灵活有序的verify;
支持详细用户号码的时间以及至少一次验证;
支持自定义的参数匹配器;
使用Mocktio编写单元测试
谈到模拟的时候只需要关心:设置测试数据、设定预期结果、验证结果
/*
1.类中导入常用包
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockObjectFactory;
2.如果涉及到静态类等,使用PowerMockito,并且在类头使用@PrepateForTest({这里是测试中使用到的静态类的预加载})
*/
@PrepareForTest({StaticClass.class})
@PowerMockIgnore({"javax.management.*"})
public class AccessProcessImplTest
{
//声明对象
private AccessProcessImpl accProcess;
private RtcMsg rtcMsg;
private IThreadModel threadModel;
@Test
public void Xxx()
{
//1初始化
//Mockito.spy()模拟被测试类,会执行到被测试类内部
//Mockito.mock(class);模拟对象,只有在调用的时候才会模拟
//@Mock注解,一般放在类的变量区域,这样对于所有的测试方法,该模拟的对象都是有效的,所以可能会导致多次调用现象,建议使用Mockito.mock()方式
accProcess = Mockito.spy(new AccessProcessImpl());
//2打桩
//模拟对象有时候会调用方法,其中的参数是使用参数匹配器的方式:Mockito.anyInt() Mockito.anyString() Mockito.any(class)最后一种是自定义方式的参数匹配器
//thenReturn可能会返回自己设定的测试数据,有一点需要注意,模拟测试数据的时候,一定是要单纯的数据模拟,自己手动构造的数据,不能使用if for等语句块构造
Mockito.when().thenReturn();
//3测试
accProcess.process(rtcMsg);//使用Mockito.spy()方式可以让模拟对象去执行对应的测试方法,同时又能够包含模拟对象
//4验证
//有两种验证方式
//1)Mockito.verify来验证对象是否执行某个方法,执行了多少次(Mockito.times(n) n次 Mockito.atLeast(n) 至少n次 Mockito.never() 一次也没有)
//2)Assert.assertEquals( obj1 , obj2 );验证两个对象是否相等
Mockito.verify(threadModel, Mockito.times(1)).method();
}
}
//必须要写的,我理解的是工厂类,用来构建模拟对象
@ObjectFactory
public ITestObjectFactory getObjectFactory()
{
return new PowerMockObjectFactory();
}
(1)单元测试的好处
经过近一周的学习,我感觉单元测试的编写,让我更好的理解了代码的运行逻辑,在此基础上也熟悉了好多插件的使用。
查找的资料表明:单元测试的好处不仅仅是我所总结的,还有重构、优化代码、集成测试等方面都有优势。
(2)对待单元测试的态度
无单测,不编码
测试驱动开发(Test Driven Developement)
(3)单元测试的覆盖率
在maven中加入jacoco插件,单元测试编写调试完成之后,只需要,maven install命令运行即可(在Eclipse中是在pom文件上右键 run as maven install)
此时,会自动在target中生成suit文件(注意在testng.xml文件中要添加相应的测试类,不然没添加的不会被统计覆盖率)
D:\Myworkspace…\badFileToMq\target\site\jacoco到相应工作空间目录下查找到target\site\jacoco\index.html文件,点开就是jacoco可视化覆盖率统计结果
(4)Mockito的覆盖率
时间有限,这点就不细致说明了。关键字 jacoco 单元测试覆盖率可以自行百度 google等。
如有表达有误之处,还望大家积极指出,谢谢!
单元测试 ↩︎
testng教程 ↩︎
Mock和Mockito简介 ↩︎
官方testng配置 文件 ↩︎