Junit4单元测试的基本用法 快速入门
- 单元测试
- 添加依赖
- Caculate 类测试
- 执行顺序
- Test的两个属性——细讲
单元测试
第一篇博客开始啦,用这个记录一下学习的过程,就相当于一个笔记,希望能一直坚持下去哦!
哈哈哈,大佬就勿看啦!这个给纯小白看的!嘻嘻嘻嘻
单元测试可以帮助我们验证程序的逻辑是否正确、可以降低bug修复的成本、更有利于代码重构等等。所以,我们在写代码的时候,尽量保证单元测试的覆盖率。能力好的可以先写测试用例,再写功能代码(测试先行)。
添加依赖
1、在build gradle(Module:app) 的dependencies添加依赖
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Caculate 类测试
1、创建一个Caculate类
里面先写四个方法,runadd(),runequal(),runmul(),runmul()
package com.example.testdemo;
public class Caculate {
public int runadd(int a, int b){
return a+b;
}
public int runequal(int a, int b){
return a-b;
}
public int runmul(int a, int b){
return a*b;
}
public int rundivide(int a,int b) throws Exception{
if(b == 0){
throw new Exception("被除数不能为0");
}else{
return a/b;
}
}
}
2、右键Caculate类名,点Go To Test
选择JUnit4,因为Android studio 已经帮我们集成好了,下面Member中的四个方法要打勾,生成需要测试的代码,点击ok
这时可以看到测试方法已经构建完了,我们开始在方法体中写具体的实现方法了。这里我写了一个CaculateTest构造方法,后面要用到滴,然后还写了个@Ignore,这后面也要用到先写上来了。
package com.example.testdemo;
import android.util.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CaculateTest {
public CaculateTest() {
System.out.println("构造方法");
}
@BeforeClass
public static void setUpBeforeClass() {
System.out.println("BeforeClass");
}
@AfterClass
public static void tearDownAfterClass() {
System.out.println("AfterClass");
}
@Before
public void setUp() throws Exception {
System.out.println("Before");
}
@After
public void tearDown() throws Exception {
System.out.println("After");
}
@Test
public void runadd() {
System.out.println("Test__runadd");
assertEquals(7, new Caculate().runadd(2,3));
// Caculate caculate =new Caculate();
// int r= caculate.runadd(2,3);
// assertEquals(r,5);
//可以把这三行代码写成一段
//assertEquals(5, new Claculate().add(2, 3));
/**assertEquals这个方法是一个断言方法
第一个参数表示预期的结果
第二个参数表示程序的执行结果
当预期结果与执行结果是一致的时候,则表示单元测试成功
**/
}
@Test
public void runequal() {
System.out.println("Test__runequal");
Caculate caculate =new Caculate();
int r= caculate.runadd(2,3);
assertEquals(r,5);
}
@Test
public void runmul(){
System.out.println("Test__runmul");
assertEquals(20,new Caculate().runmul(4,5));
}
@Ignore
@Test(expected = Exception.class)
public void rundivide() throws Exception{
System.out.println("Test__rundivide");
assertEquals(2,new Caculate().rundivide(4,2));
fail("被除数不能为0");
}
}
上面我把2+3的结果故意写错,写成7,这样在运行test时,它就会报错,更方便我们了解。
执行Test代码,可以执行所有代码,在CaculateTest右键,run Test
执行结果如下:
如果我们把7,改为5,那么运行就不会报错了,运行成功结果如下:
执行顺序
JUnit4利用JDK5的新特性Annotation,使用注解来定义测试规则。这里讲一下以下几个常用的注解:
- @Test:把一个方法标记为测试方法
- @Test(excepted=xx.class): xx.class表示异常类,表示测试的方法抛出此异常时,认为是正常的测试通过的
- @Test(timeout=毫秒数) :测试方法执行时间是否符合预期
- @Before:每一个测试方法执行前自动调用一次
- @After:每一个测试方法执行完自动调用一次
- @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰,通常进行一些资源的加载。
- @AfterClass:所有测试方法执行完执行一次,在测试类还没有实例化就已经被加载,所以用static修饰,通常用来释放资源。
- @Ignore:暂不执行该测试方法
- @RunWith:可以更改测试运行器org.junit.runner.Runner
- Parameters:参数化注解
package com.example.testdemo;
import android.util.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CaculateTest {
public CaculateTest() {
System.out.println("构造方法");
}
@BeforeClass
public static void setUpBeforeClass() {
System.out.println("BeforeClass");
}
@AfterClass
public static void tearDownAfterClass() {
System.out.println("AfterClass");
}
@Before
public void setUp() throws Exception {
System.out.println("Before");
}
@After
public void tearDown() throws Exception {
System.out.println("After");
}
@Test
public void runadd() {
System.out.println("Test__runadd");
assertEquals(5, new Caculate().runadd(2,3));
// Caculate caculate =new Caculate();
// int r= caculate.runadd(2,3);
// assertEquals(r,5);
//可以把这三行代码写成一段
//assertEquals(5, new Claculate().add(2, 3));
/**assertEquals这个方法是一个断言方法
第一个参数表示预期的结果
第二个参数表示程序的执行结果
当预期结果与执行结果是一致的时候,则表示单元测试成功
**/
}
@Test
public void runequal() {
System.out.println("Test__runequal");
Caculate caculate =new Caculate();
int r= caculate.runadd(2,3);
assertEquals(r,5);
}
@Test
public void runmul(){
System.out.println("Test__runmul");
assertEquals(20,new Caculate().runmul(4,5));
}
@Ignore
@Test(expected = Exception.class)
public void rundivide() throws Exception{
System.out.println("Test__rundivide");
assertEquals(2,new Caculate().rundivide(4,2));
fail("被除数不能为0");
}
}
运行结果
-
@BeforeClass和@AfterClass在类被实例化前(构造方法执行前)就被调用了,而且只执行一次,通常用来初始化和关闭资源。
-
@Before和@After和在每个@Test执行前后都会被执行一次。
-
@Test标记一个方法为测试方法没什么好说的,被@Ignore标记的测试方法不会被执行,例如这个模块还没完成或者现在想测试别的不想测试这一块。
-
以上有一个问题,构造方法居然被执行了三次次。所以我这里要说明一下,JUnit4为了保证每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会重新实例化测试类。
Test的两个属性——细讲
我们都能看到上面Test中有两个属性,一个异常类,还有个检测时间逾期问题,那么下面通过一个例子带大家详细了解一下。
我们可以看到在Caculate类里面有一个rundivide()方法,执行除法,运算时时不允许被除数为0的,这时候就要抛出异常
public int rundivide(int a,int b) throws Exception{
if(b == 0){
throw new Exception("被除数不能为0");
}else{
return a/b;
}
}
然后去CaculateTest测试类中,将@Ignore删除
@Test(expected = Exception.class)
public void rundivide() throws Exception{
System.out.println("Test__rundivide");
assertEquals(2,new Caculate().rundivide(4,2));
fail("被除数参数为0没有抛出异常");
}
我们将被除数设置为不为0的数,执行结果如下
将参数设为0,结果如下
这个方法就是(expected = Exception.class)和fail(“被除数参数为0没有抛出异常”);之间的配合。就是这个测试方法会检查是否抛出Exception异常(当然也可以检测是否抛出其它异常),如果抛出了异常那么测试通过(因为你的预期就是传进负数会抛出异常)。没有抛出异常则测试不通过执行fail(“被除数参数为0没有抛出异常”);
然后说下timeout属性,这个是用来测试性能的,就是测试一个方法能不能在规定时间内完成。
在Caculate类里面添加一个新的方法sort(),创建一个数组排序的方法,用的是冒泡排序。
public void sort(int[] arr) {
//冒泡排序
for (int i = 0; i < arr.length - 1; i++) { //控制比较轮数
for (int j = 0; j < arr.length - i - 1; j++) { //控制每轮的两两比较次数
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
接下来在CaculateTest类中添加测试方法,随机生成一个长度为50000的数组然后测试排序所用时间。timeout的值为2000,单位和毫秒,也就是说超出2秒将视为测试不通过。
@Test(timeout = 2000)
public void runSort() throws Exception {
int[] arr = new int[50000]; //数组长度为50000
int arrLength = arr.length;
//随机生成数组元素
Random r = new Random();
for (int i = 0; i < arrLength; i++) {
arr[i] = r.nextInt(arrLength);
}
new Caculate().sort(arr);
}
运行CaculateTest,测试结果不通过,
那接下来,该怎么办呢?我想大家应该都知道了吧,将sort()方法进行改进,用一种快速的排序方法——快速排序
public void sort(int[] arr) {
//快速排序
if (arr.length <= 1) {
return;
} else {
partition(arr, 0, arr.length - 1);
}
}
static void partition(int[] arr, int left, int right) {
int i = left;
int j = right;
int pivotKey = arr[left]; //基准数
while (i < j) {
while (i < j && arr[j] >= pivotKey) {
j--;
}
while (i < j && arr[i] <= pivotKey) {
i++;
}
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
if (i != left) {
arr[left] = arr[i];
arr[i] = pivotKey;
}
if (i - left > 1) {
partition(arr, left, i - 1);
}
if (right - j > 1) {
partition(arr, j + 1, right);
}
}
再运行一次,就完美通过啦!