Java单元测试框架JUnit的基本使用
单元测试十分重要!尤其是微服务web之类的复杂项目,这类项目代码的嵌套层次多,方法层数多,如果不做单元测试,功能完成之后bug的数量级肯定会很大。如果你有非常严格的单元测试,每写一个方法,每完成一模块或者功能,都进行了单元测试,那么在业务功能整体完成之后,你就会发现你基本不可能出现bug。单元测试的好处非常多,还可以极大的减少程序后期的维护成本和工作量。有了成套的单元测试,后期改代码排查问题,你就不用一遍一遍的读代码了,直接跑一遍单元测试就知道大概了。
文章目录
1. 安装使用
maven配置,简单而俗套
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
2. 实例待测试代码
一个别人写好的计算类,做实例用的,参考价值还可以,拿来直接用。
public class Calculator {
public int calculate(String expression) {
String[] ss = expression.split("\\+");
System.out.println(expression + " => " + Arrays.toString(ss));
int sum = 0;
for (String s: ss) {
sum += Integer.parseInt(s.trim());
}
return sum;
}
}
3. 编写测试类
大体的结构基本在创建项目的时候,Maven原型已经给你创建好了,具体的细节目录和包结构因人而异,可以随意发挥。
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
@Test
public void calculate() {
assertEquals(4, new Calculator().calculate("1 + 2"));
assertEquals(6, new Calculator().calculate("1 + 2 + 3"));
}
}
4. 常用的断言方法
- assertEquals(100, x): 断言相等
- assertArrayEquals({1, 2, 3}, x): 断言数组相等
- assertEquals(3.1416, x, 0.0001): 浮点数组断言相等
- assertNull(x): 断言为null
- assertTrue(x > 0): 断言为true
- assertFalse(x < 0): 断言为false;
- assertNotEquals: 断言不相等
- assertNotNull: 断言不为null
5. 单元测试逻辑结构
- 在@Before方法中初始化测试资源
- 在@After方法中释放测试资源
- @BeforeClass: 初始化非常耗时的资源, 例如创建数据库
- @AfterClass: 清理@BeforeClass创建的资源, 例如创建数据库
- 对于每一个@Test方法的执行顺序
- 单个@Test方法执行前会创建新的XxxTest实例, 实例变量的状态不会传递给下一个@Test方法,
- 单个@Test方法执行前后会执行@Before和@After方法
- 执行顺序
- 执行类的构造函数=>执行@Before方法=>执行@Test方法=>执行@After方法
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class SequenceTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
System.out.println("BeforeClass()");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
System.out.println("AfterClass()");
}
@Before
public void setUp() throws Exception {
System.out.println(" Before()");
}
@After
public void tearDown() throws Exception {
System.out.println(" After()");
}
public SequenceTest() {
System.out.println(" new SequenceTest()");
}
@Test
public void testA() {
System.out.println(" testA()");
}
@Test
public void testB() {
System.out.println(" testB()");
}
}
6. 参数化测试
@RunWith: 当类被@RunWith注释修饰, 或者类继承了一个被该注解类修饰的类, JUnit将会使用这个注解所指明的运行器(runner)来运行测试, 而不是JUni默认的运行器
要进行参数化测试,需要在类上面指定如下的运行器:@RunWith (Parameterized.class)
然后,在提供数据的方法上加上一个@Parameters注解,这个方法必须是静态static的,并且返回一个集合Collection。
具体详细说明如下:
- 测试类必须由Parameterized测试运行器修饰
- 准备数据。数据的准备需要在一个方法中进行,该方法需要满足一定的要求:
- 该方法必须由Parameters注解修饰
- 该方法必须为public static的
- 该方法必须返回Collection类型
- 该方法的名字不做要求
- 该方法没有参数
@RunWith(Parameterized.class)
public class TestParams {
@Parameterized.Parameters
public static Collection<?> data() {
return Arrays.asList(new Object[][] { { "1+2", 3 }, { "1+2+3", 8 }, { "123+456", 579 }, { " 1 + 5 + 10 ", 16 } });
}
Calculator calc;
@Parameterized.Parameter(0)
public String input;
@Parameterized.Parameter(1)
public int expected;
@Before
public void setUp() {
calc = new Calculator();
}
@Test
public void testCalculate() {
int r = calc.calculate(this.input);
assertEquals(this.expected, r);
}
}
7. 其他常用测试
7.1 异常测试
异常测试可以通过@Test(expected=Exception.class), 对可能发生的每种类型的异常进行测试
- 如果抛出了指定类型的异常, 测试成功
- 如果没有抛出指定类型的异常, 或者抛出的异常类型不对, 测试失败
// 发生了ArithmeticException异常, 代码通过
@Test(expected = ArithmeticException.class)
public void testException() {
int i = 1 / 0;
}
7.1 超时设置
在测试类的方法上使用 @Timeout 注解,测试类的所有方法应用 Timeout 规则
@Test(timeout=1000)可以设置超时时间,单位是毫秒
@Timeout 注解来测试任意特定方法的执行时间。如果测试方法的执行时间大于指定的超时参数,测试方法将抛出异常,测试结果为失败。指定的超时参数是以毫秒记。
mport org.junit.*;
public class TimeOutTest {
@Test(timeout = 1000)
public void infinity() {
while (true);
}
}