Szhangbiao's blog

记录一些让自己可以回忆的东西

0%

使用JUnit4进行单元测试

我们写单元测试,一般都会用到一个或多个单元测试框架,在这里,我们介绍一下 JUnit4 这个测试框架。其他的很多框架,包括我们后面会看到的 Robolectric,都是基于或兼容 JUnit4 的。

什么是 JUnit 框架

JUnit 框架是 Java 界用的最广泛、最基础的一个框架,其他的更多框架都是基于或兼容 Junit 框架的,JUnit 最新的版本是 JUnit5,但是 JUnit4 是使用最多的

在上篇的简单的例子中,测试的方法是通过@Test注解来标志的,只要有这个注解,Junit 就会把这个方法当成一个测试方法,测试方法的名字当然可以随便取,但是为了更有可读性,都会跟被测试的方法一致

JUnit 如何使用

下面我们来一个更能全面了解 JUnit 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CalculatorUtil {
fun add(numberOne: Int, numberTwo: Int): Int {
return numberOne + numberTwo
}

fun multiply(number: Int, multiplier: Int): Int {
return number * multiplier
}

fun divide(number: Int, divider: Int): Int {
return number / divider
}
}

相应的测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class CalculatorUtilTest {

private var calculator: CalculatorUtil? = null

@Before
fun setUp() {
calculator = CalculatorUtil()
}

@Test
fun testAdd() {
val addSum = calculator?.add(1, 1)
Assert.assertEquals(2, addSum)
}

@Test
@Ignore("Not implemented yet")
fun testMultiply() {

}

@Test
fun testDivideGenerally() {
val divideNum = calculator?.divide(4, 2)
Assert.assertEquals(2, divideNum)
}

@Test(expected = ArithmeticException::class)
fun testDividerThrows() {
calculator?.divide(4, 0)
}

@After
fun tearDown() {
calculator = null
}
}

可以看到类的初始化放到了@Before注解的方法里,相对应的也有一个@After注解的方法里去把对象置空,这两个注解是 JUnit4 提供的,会在运行一个测试方法前后调用,分别用来做准备工作和释放资源。

由于类的初始化是每个测试方法都涉及的,我们可以把它放到@Before注解的方法里。@Ignore注解的测试方法是暂时还没写或者没写完的测试方法,当跑所有的单元测试的时候 JUnit 会忽略@Ignore注解的测试方法的运行。

有时候抛出异常是一个方法正常工作的一部分,为了能校验这一情况我们可以在注解里添加参数 @Test(expected = XXXException::class) 这样就能对某些会抛出异常的方法进行单元测试。

JUnit 更多的 API 介绍

下面列出Assert下的常用校验方法,大家可以熟悉一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assertEquals(expected, actual) // 验证 expected 的值跟 actual 是一样的,如果是一样的话,测试通过,不然的话,测试失败。如果传入的是 object,那么这里的对比用的是 equals()

assertEquals(expected, actual, tolerance) // 这里传入的 expected 和 actual 是 float 或 double 类型的,所以这里运行传入一个偏差值。如果两个数的差异在这个偏差值之内,则测试通过,否者测试失败。

assertTrue(condition) // 验证 condition 的值是 true

assertFalse(condition) //验证 condition 的值是 false

assertNull(any) // 验证 any 的值是 null

assertNotNull(any) // 验证 any 的值不是 null

assertSame(expected, actual) // 验证 expected 和 actual 是同一个对象,即指向同一个对象

assertNotSame(expected, actual) // 验证 expected 和 actual 不是同一个对象,即指向不同的对象

fail() // 让测试方法失败

其中fail()方法我们可以用来验证我们的测试代码是否是真的运行了的,还有一个作用是跟@Test(expected = XXXException::class)的作用类似

注意:上面每一个方法都有一个重载的方法,就是可以增加一个 String 类型的参数在方法的第一个参数上,表示如果验证失败的话,将用这个字符串作为失败的结果报告。
> assertEquals("Current result should be 2", 2, addSum)当 assertEquals 校验失败时在结果报告里面将显示 Current result should be 2 这样可以让测试结果更具有可读性。

小结

JUnit 的使用,相对来说是比较简单,也是比较容易理解的其中 Assert 部分,可以帮我们验证一个方法的返回结果。然而,这些只能帮我们测试有返回值的那些方法。一个类的方法分两种,一是有返回值的方法,这些可以通过我们今天讲的 JUnit 来做测试。而另外一种没有返回值的方法,即 void 方法,则要通过另外一个框架,Mockito/mockk,来验证它的正确性。