> 本章介绍 JUnit4 的最佳实践,常用注解以及运行流程
1. 完善代码——JUnit最佳实践
```java
package com.dodoke;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 计算器测试类
*/
public class CalculatorTest {
private Calculator calculator;
// 执行测试方法之前调用,初始化计算器类实例
@Before
public void setUp() throws Exception {
calculator = new Calculator();
}
// 测试加法
@Test
public void testSum() throws Exception {
long result = calculator.sum(10, 3);
Assert.assertEquals(13L, result);
}
// 测试减法
@Test
public void testSubtract() throws Exception {
long result = calculator.subtract(10, 3);
Assert.assertEquals(7L, result);
}
// 测试乘法
@Test
public void testMultiply() throws Exception {
long result = calculator.multiply(10, 3);
Assert.assertEquals(30L, result);
}
// 测试除法
@Test
public void testDivide() throws Exception {
double result = calculator.divide(10, 2);
Assert.assertEquals(10 / 2, result, 1E-10);
}
}
```
- 测试方法上必须使用 @Test 注解修饰
- 测试方法必须使用 public void 进行修饰,不能带任何的参数
- 新建一个源代码目录来存放我们的测试代码,即将测试代码和项目业务代码分开
- 测试类所在的包名应该和被测试类所在的包名保持一致
- 测试单元中的每个方法必须可以独立测试,测试方法间不能有任何的依赖
- 测试类使用 Test 作为类名的后缀(不是必须)
- 测试方法使用 test 作为方法名的前缀(不是必须)
2. 测试失败的两种情况
- 测试用例不是用来证明你是对的,而是用来证明你没有错。
- 测试用例用来达到想要的预期结果,但对于逻辑错误无能为力。
- Failure: 一般有单元测试使用的断言方法判断失败所引起的。这仅仅表示测试点发现了问题,就是说程序输出结果和我们预期的不一样。
- Error: 是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中的bug。
```java
package com.dodoke;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 错误和失败测试类
*/
public class ErrorAndFailureTest {
private Calculator calculator;
@Before
public void setUp() throws Exception {
calculator = new Calculator();
}
@Test
public void testDivide() throws Exception {
Assert.assertEquals(10.0 / 3, calculator.divide(10, 3), 1E-10);
// Assert.assertEquals(10.0 / 0, calculator.divide(10, 0), 1E-10);
}
}
```
```
// 运行结果 1
java.lang.AssertionError:
Expected :3.3333333333333335
Actual :3.0
```
```
// 运行结果 2
java.lang.ArithmeticException: by zero
```
3. JUnit运行流程
- @BeforeClass: 修饰的方法必须要是静态方法,会在所有方法被调用前被执行,当测试类被加载后接着就会运行它,而且在内存中它只会存在一份实例,它比较适合加载配置文件。
- @AfterClass: 修饰的方法必须要是静态方法,会在所有测试结束之后运行,通常会用来对资源的清理,如关闭数据库的连接。
- @Before: 每一个测试方法之前运行
- @After: 每一个测试方法之后运行
```java
package com.dodoke;
import org.junit.*;
/**
* JUnit流程测试类
*/
public class JUnitFlowTest {
@BeforeClass
public static void testBeforeClass() throws Exception {
System.out.println("BeforeClass");
}
@AfterClass
public static void testAfterClass() throws Exception {
System.out.println("AfterClass");
}
@Before
public void testBefore() throws Exception {
System.out.println("Before");
}
@After
public void testAfter() throws Exception {
System.out.println("After");
}
@Test
public void testTest() throws Exception {
System.out.println("Test");
}
}
```
```
BeforeClass
Before
Test
After
AfterClass
```
4. JUnit常用注解
- @Test: 将一个普通的方法修饰成为一个测试方法
- @Test(expected=XXException.class) 预期异常
- @Test(timeout=xx(毫秒)) 耗费时间
- @BeforeClass: 它会在所有的方法运行前被执行,static修饰
- @AfterClass: 它会在所有的方法运行后被执行,static修饰
- @Before: 会在每一个测试方法被运行前执行一次
- @After: 会在每一个测试方法运行后执行一次
- @Ignore: 所修饰的方法会被测试运行器忽略
- @RunWith: 可以更改测试运行器 org.junit.runner.Runner
```java
@Test(expected = ArithmeticException.class)
public void testExpected() throws Exception {
double result = new Calculator().divide(10, 0);
Assert.assertEquals(10.0 / 3, result, 1E-10);
}
@Test(timeout = 1)
public void testTimeout() throws Exception {
long result = new Calculator().sum(10, 3);
Assert.assertEquals(13L, result);
}
@Ignore
public void testIgnore() throws Exception {
System.out.println("Ignore");
}
```
```
// testTimeout 测试结果
org.junit.runners.model.TestTimedOutException: test timed out after 1 milliseconds
```