1.JUnit 4 概述:
单元测试是软件开发中必不可少的一环,但是在平常开发中往往因为项目周期紧,工作量大而被选择忽略,这样往往导致软件问题层出不穷。
线上出现的不少问题其实在有单元测试的情况下就可以及时发现和处理,因此培养自己在日常开发中写单元测试的能力是很有必要的。无论是对自己的编码能力的提高,还是项目质量的提升,都是大有好处,本文将介绍 Java 单元测试框架 JUnit 4 的基础认识和使用来编写单元测试,希望同样对你有所帮助。
JUnit 是一个开源的 Java 语言的单元测试框架。使用断言(Assertion)测试期望结果,可以方便组织和运行测试。
JUnit 设计:
- TestCase:一个TestCase表示一个测试
- TestSuite:一个TestSuite 包含一组 TestCase,表示一组测试
- TestFixture:一个TestFixture表示一个测试环境
- TestResult:用于收集测试结果
- TestRunner:用于运行测试
测试方案设计:
- 一个TestCase包含一组测试方法
- 使用断言测试结果(注意浮点数assertEquals要指定delta)
- 每个测试方法必须独立
- 测试代码必须简单。
- 不能为测试代码再编写测试
- 测试要覆盖各种输入条件,特别是边界条件。
安装 IDEA 插件:
进入设置,对测试用例生成代码模板进行修改:
生成路径更改为:
${SOURCEPATH}/../../test/java/${PACKAGE}/${FILENAME}
修改生成模板,去掉 test 前路径:
2.JUnit 常用注解:
2.1 Assert 断言常用方法:
- 断言相等:
assertEquals(期望值, 测试值);
- 断言数组相等:
assertArrayEquals(期望值, 测试值);
- 断言浮点数:
assertEquals(期望值, 测试值, 小数位);
- 断言为空:
assertNull(测试值);
- 断言不为空:
assertNotNull(测试值);
- 断言为true/fasle:
assertTrue(表达式); assertFasle(表达式);
- 其他:
assertNotEquals();
2.2 测试方法生成 @Test:
下面使用的是IDEA自带的 JUnit 测试框架代码生成
对已经编写好的方法进行单元测试:
public class TestCaseDemo01 { public String sum(String str, int ... numbers) { int intStr = Integer.parseInt(str); int sum = 0; for (int i: numbers) { sum += i; } return sum + " " + str; } public static void main(String[] args) { TestCaseDemo01 caseDemo01 = new TestCaseDemo01(); String sum = caseDemo01.sum("1314", 10, 20, 30); System.out.println("结果:" + sum); } }
上面是已经编写好的方法,现在需要对sum方法进行单元测试,需要先生成单元测试类:
- 选中当前类名,单击鼠标右键(快捷键:Alt + Insert,PS:有些键盘需要加 Fn 进行转换)
- 选择测试:
- 配置生成测试类基本信息:
- 生成完成,然后在对应方法中编写具体的测试实现:
import org.junit.Test; import static org.junit.Assert.*; public class TestCaseDemo01Test { @Test public void sum() { assertEquals("12 3", new TestCaseDemo01().sum("3", 10, 2)); assertEquals("123", new TestCaseDemo01().sum("3", 10, 2)); } @Test public void main() { } }
2.3 @Before 和 @After:
同一个单元测试内的多个测试方法:测试前都需要初始化某些对象,测试后可能需要清理资源。JUnit 使用 @Before 和 @After 可以实现,在@Before 注解方法中初始化测试资源。在 @After 注解方法中释放资源。
JUnit 对每个 @Test 测试方法执行过程:
- 实例化 CalCulatorTest
- 执行 @Before 方法
- 执行 @Test 方法
- 执行 @After 方法
注意:使用@Before 和 @After 可以保证:
- 单个 @Test 方法执行前会创建新的 XxxTest 实例,实例变量的状态不会传递给下一个 @Test 方法。
- 单个 @Test 方法执行前后会执行 @Before 和 @After 方法。
package wiki.csbox.testdemo.junit4; import org.junit.Test; import org.junit.Before; import org.junit.After; /** * TestCaseDemo01 Tester. * * @author <Authors name> * @since <pre>1月 5, 2023</pre> * @version 1.0 */ public class TestCaseDemo01Test { @Before public void before() throws Exception { System.out.println("before running......"); } @Test public void testSum() throws Exception { System.out.println("testSum running......"); } @Test public void testMain() throws Exception { System.out.println("testMain running......"); } @After public void after() throws Exception { System.out.println("after running......"); } }
2.4 @BeforeClass 和 @AfterClass:
使用 @BeforeClass 和 @AfterClass 静态方法上:
- 在执行所有 @Test 方法前执行 @BeforeClass 标注的静态方法
- 执行所有测试
- 在执行所有 @Test 方法后执行 @AfterClass 标注的静态方法
注意:@BeforeClass 静态方法初始化的对象只能存放在静态字段中,静态字段的状态会影响到所有的 @Test 测试方法的测试环境。
2.5 JUnit 执行测试生命周期:
- @BeforeClass:初始化耗时资源,以静态变量存放
- @Before:用于初始化测试对象,测试对象以实例变量存放
- @After:用于清理 @Before 创建的对象
- @AfterClass:用于清理@BeforeClass创建的资源
3.异常测试:
对于可能抛出异常的方法进行测试,异常本身是方法签名的一部分:
@Test(expected = Exception.class)
虽然可以使用 try-catch 测试,但是这样会写出很多语句,从而使其变得复杂起来。
4.超时测试:
可以为JUnit 的单个测试设置超时时间,如果测试方法执行时间小于超时设置的时间,测试通过;反之,测试不通过。
@Test(timeout = 1000)
注意:超时测试不能取代性能测试和压力测试!!!
5.参数化测试:
参数化测试,将需要测试的所有参数组织起来,用不同的测试数据,调用相同的方法进行测试。
测试参数的要求:
- 参数必须有静态方法
data()
返回,返回类型为 Collection<?> 静态方法必须标记为@Parameters
- 测试类必须标记为:
@Runwith(Parameterized.class);
- 构造方法参数必须和测试参数对应
import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; @RunWith(Parameterized.class) public class DemoAdsTest { @Parameterized.Parameters public static Collection<?> data() { return Arrays.asList(new Object[][]{ {0, 0}, {1, 1}, {10, -10} }); } int input; int expect; public DemoAdsTest(int input, int expect) { this.input = input; this.expect = expect; } @Test public void testAbs() { int i = Math.abs(this.input); Assertions.assertEquals(this.expect, i); } }