如何更好的编写JAVA单元测试
如各位希望转载或引用,请注明出处,尊重原创,谢谢。如有疑问或错误,欢迎邮件沟通。
gitHub地址:https://github.com/thinkingfioa
邮箱地址:thinking_fioa@163.com
博客地址: https://blog.youkuaiyun.com/thinking_fioa
gitHub项目地址:https://github.com/thinkingfioa/tech-summary
gitHub项目代码地址:https://github.com/thinkingfioa/tech-summary-code
本文重点
单元测试覆盖率往往是检验一个系统的可靠性指标,优秀的单元测试能帮助系统提早发现问题。JAVA语言提供了非常好的单元测试框架,本文将重点介绍: 如何编写单元测试Mock+PowerMock使用
1. 背景
开发过程中,很多重要的业务和关键性代码,都需要单元测试覆盖,这样能保证质量。
好的单元测试用例,能有效提高软件的质量,也方便后期的代码重构和维护。但是一般来说编写单元测试工作量很大,单元测试代码维护成本也很高,因为你业务逻辑发生了改变,代码结构发生了改变,不可能不会修改单元测试。
通常单元测试的代码,阅读难度也很高,需要理解具体的业务逻辑。建议编写单元测试时,当需要使用其他类的时候,尽量使用Mock方法,做到单元测试依赖越少,后续修改和理解就更简单。
2. 编写单元测试的原则
2.1 单元测试类包路径管理
建议单元测试中的test包路径目录结构与main包中的目录结构保持一致。
2.2 单元测试类名称管理
建议每个单元测试类测试功能单一,仅针对性测试指定类的方法。比如文件Father.java中类名称为Father,那么我们在test新建一个相同的包结构目录,并在新建后的目录下新建FatherTest.java文件,类名为FatherTest。
单元测试中每个测试方法以testXXX()开头
Father.java
package org.thinking.fioa;
public class Father {
public void growUp() throws Exception {
}
}
FatherTest.java
package org.thinking.fioa;
public class FatherTest {
public void testGrowUp() throws Exception {
}
}
3. POM依赖
JAVA语言下单元测试比不可少三个依赖包,需要配置到pom.xml下。powermock + mockito + junit。
<!-- unit -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.20.1</version>
<scope>test</scope>
</dependency>
<!-- unit end -->
4. 基础知识
Java使用的Junit4常用的annotation
4.1 静态方法
@BeforeClass ----- 针对所有测试,只执行一次。方法签名必须是static void
@AfterClass ----- 针对所有测试,只执行一次。方法签名必须是static void
4.2 非静态方法
@Before ----- 初始化方法。每个@Test测试方法前都会执行一次
@Test ----- 测试方法。每个@Test测试方法都会创建一个实例对象
@After ----- 释放资源。每个@Test测试方法后都会执行一次
4.3 执行顺序
@BeforeClass -> {类的构造函数 -> @Before -> @Test -> @After} , 类的构造函数 -> {@Before -> @Test -> @After} … -> @AfterClass
其中每个@Test方法执行前会创建新的XxxTest实例, 单个@Test方法执行前后会执行@Before和@After方法
4.4 断言的常方法
assertEquals(100, x) ----- 相等
assertArrayEquals(100, x) ----- 数组相等
assertNull(x) ----- 断言为null
assertTrue(x) ----- 断言为true
assertNotEquals ----- 断言不相等
expected = Exception.class ----- 异常测试
timeout=1000 ----- 超时时间
5. 单元测试编写参考用例
详细代码可参考tech-summary-code
下面举例介绍四大类单元测试方法,这四类单元测试用例能基本满足大家日常编写单元测试的功能
序号 | 名称 | 说明 |
---|---|---|
1 | 基础单元测试 | 基础用例 |
2 | 使用单例设计模式 | 单例是开发中最长使用的设计模式 |
3 | 依赖其他类 | 面向对象语言,封装是一大特性 |
4 | 类对象都是私有属性 | 类的属性都是私有,或者方法是私有的,可通过反射方法来编写单元测试 |
5.1 基础单元测试用例
基础单元测试是最被经常使用的
5.1.1 类CommonMethod.java
测试类CommonMethod.java有如下的对象公开方法,为每个方法编写单元测试
public class CommonMethod {
public boolean success() {
return true;
}
public int age() {
return 100;
}
public int[] arrayOfInt() {
return new int[]{
1, 2, 3, 4};
}
public Object isNull() {
return null;
}
public void throwException() {
throw new NullPointerException();
}
public void timeout() throws InterruptedException {
Thread.sleep(500L);
}
}
5.1.2 测试类CommonMethodTest.java
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class CommonMethodTest {
private CommonMethod method;
/**
* 仅执行一次
*/
@BeforeClass
public static void