单元测试整理-分享
PS:未完待续,持续更新中。。。
先简单说一下为什么要写测试用例
1. 可以避免测试点的遗漏,为了更好的进行测试,可以提高测试效率
2. 可以自动测试,可以在项目打包前进行测试校验
3. 可以及时发现因为修改代码导致新的问题的出现,并及时解决
1. 单元测试介绍:
单元测试是编写的一小段代码,用于检验目标代码的一个很小的、很明确的功能模块是否正确。为了证明这段代码的行为和我们期望的是否一致。目前的单元测试框架有JUnit、Mockito、Unitils,其中Junit是一个基于xUnit的测试框架,是目前进行单元测试的主要工具。
单元测试推荐的一本书是《JUnit实战(第2版)》。
2. idea环境相关介绍:
一个java项目应该有三个路径,Sources, Test和Resources三个部分,Sources是你的开发代码,Test是你的测试代码,Resources是你的资源文件或配置文件。
在class类名字处打开IntelliJ的quickLips,就可以看到如下的画面:
然后我们选择Create Test, 接下来会出现
idea就会生成一个这样的文件
单元测试的运行界面为
绿条表示程序正常的运行且通过了,如果为红条则表示程序中存在bug不可以正常的运行。其中console也可以显示你的所有输出。当然你也可以在非测试方法的地方右击选行运行整个测试类。
3. 单元测试基本概念:
1. JUnit基本注解介绍:
//在所有测试方法前执行一次,一般在其中写上整体初始化的代码
@BeforeClass
//在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码
@AfterClass
//在每个测试方法前执行,一般用来初始化方法(比如我们在测试别的方法时,类中与其他测试方法共享的值已经被改变,为了保证测试结果的有效性,我们会在@Before注解的方法中重置数据)
@Before
//在每个测试方法后执行,在方法执行完成后要做的事情
@After
// 测试方法执行超过1000毫秒后算超时,测试将失败
@Test(timeout = 1000)
// 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败
@Test(expected = Exception.class)
// 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类
@Ignore(“not ready yet”)
@Test
@RunWith
2. Junit4断言
实现数组比较断言
String[] users = new String[]{"tom", "sharp", "tony"};
Assert.assertArrayEquals(new String[]{"tom", "sharp", "tony"}, users);
3. 异常测试
//预期抛出空指针异常
@Test(expected=NullPointerException.class)
public void method() {
assertNotNull(user.getUserName());
}
4. 超时测试
@Test(timeout = 10)//10ms
public void method() {
}
4. controller层单元测试:
环境配置:
@RunWith(SpringJUnit4ClassRunner.class) //指定测试启动器
@SpringApplicationConfiguration(classes = Application.class)//指定SpringBoot工程的Application启动类
@WebAppConfiguration //Web项目中,Junit需要模拟ServletContext,由该注解提供
@ActiveProfiles("test")
public class V2MerchantControllerTest {}
备注:如果配置不正确会导致运行失败。
对模块进行集成测试时,希望能够通过输入URL对Controller进行测试,如果通过启动服务器,建立http client进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,这样会导致测试无法进行,为了可以对Controller进行测试,可以通过引入MockMVC进行解决。
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
MockMvc的初始化工作
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setUp() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
获取商户列表的一个测试用例:
@Test
public void getMerchantList() throws Exception {
//mockMvc.perform(get("/v2/merchants/list"));//执行一个RequestBuilder请求
mockMvc.perform( //执行一个RequestBuilder请求
get("/v2/merchants/") //请求的URL, 请求的方法是get
.contentType(requestcontentType) //数据的格式
.param("userid", "2") //添加参数
.param("token", "1") //登录凭证
.param("type", "2") //1、查自己, 2、查全部
.param("page", "1") //默认从1开始
.param("limit", "20") //每页条目数, 默认20条
)
.andExpect(status().isOk()) //返回的状态是200
.andDo(print())//打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
}
5. service层单元测试
同controller一样的配置
示例代码:
//com.nuanyou.bdpal.service.UserService
@Test
public void findCountryCodeByUserId() throws Exception {
System.out.println("输出结果为:");
System.out.println(userService.findCountryCodeByUserId(14L));
System.out.println("输出结束");
}
6. dao层单元测试
DAO层的大部分工作都是对数据库中相应的表进行CRUD操作。
示例代码:
//com.nuanyou.bdpal.dao.bd.EntityBdCertificateGroupDao
@Test
public void findByCountryIdAndUserId() throws Exception {
System.out.println("输出的结果为:");
System.out.println(dao.findByCountryIdAndUserId(1L, 14L));
}
注意事项:
JUnit非常智能的,他将你的测试程序执行完成之后,会回滚所有的操作
,做到了测试代码不影响项目程序的要求,也就解释了为什么你测试程序运行后去查看数据库并没有变化,因为所有的操作都被回滚了。你可以通过看测试的状态条是否为绿色来判断程序的正确性,或者加入几条输入语句来confirm一下。
7. 单元测试三十六计
实际进行单元测试时,具体的情形分为很多种,实际操作比理论更加丰富。总体单元测试分为mock形式和调用真实的依赖两种,mock形式单元测试将另起一篇博客,本文主要介绍调用真实依赖时一些特殊情形的处理。
1. mock形式进行单元测试
模拟mock方式进行单元测试
2. 调用真实依赖进行单元测试
- 对private私有方法的单元测试
对私有方法单元测试的场景比较少,目前并没有测试框架进行太好的支持,本文主要采用反射的方式进行单元测试,适用于比较简单的私有方法,对复杂的私有方法进行单元测试将是一场噩梦。具体的代码示例如下:
//实例化
Constructor<SharpDemo> constructor = SharpDemo.class.getDeclaredConstructor();
constructor.setAccessible(true);
SharpDemo sharp = constructor.newInstance();
//私有方法
Method method = sharp.getClass().getDeclaredMethod("privateMethodName", 参数1.class, 参数2.class...);
method.setAccessible(true);
//执行方法
ResultBean result = (ResultBean) method.invoke(sharp, "12345678", "abcd"...);
8. 注意事项:
JUnit注意事项
1. 测试方法上必须使用@Test进行修饰。
2. 测试方法必须使用public void进行修饰,不能带任何的参数。
3. 新建一个源代码目录来存放我们的测试代码。
4. 测试类的包应该和被测试类保持一致。
5. 测试单元中的每个方法必须可以独立测试,测试方法不能有任何的依赖。
可能遇到的问题:
1. 配置环境不正确导致运行失败
2. 测试方法应该为public
测试失败的两种情况
1. Failure一般由单元测试使用的断言方法判断失败所引起的,这表示测试点发现了问题,就是说程序输出的结果和我们预期的不一样。
2. Error是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中的一个隐藏的bug。