在一个单元测试中,我们经常编写多个@Test
方法,来分组、分类对目标代码进行测试。
在测试的时候,我们经常遇到一个对象需要初始化,测试完可能还需要清理的情况。如果每个@Test
方法都写一遍这样的重复代码,显然比较麻烦。
JUnit提供了编写测试前准备、测试后清理的固定代码,我们称之为Fixture。
我们来看一个具体的Calculator
的例子:
public class Calculator { private long n = 0; public long add(long x) { n = n + x; return n; } public long sub(long x) { n = n - x; return n; } }
这个类的功能很简单,但是测试的时候,我们要先初始化对象,我们不必在每个测试方法中都写上初始化代码,而是通过@BeforeEach
来初始化,通过@AfterEach
来清理资源:
public class CalculatorTest { Calculator calculator; @BeforeEach public void setUp() { this.calculator = new Calculator(); } @AfterEach public void tearDown() { this.calculator = null; } @Test void testAdd() { assertEquals(100, this.calculator.add(100)); assertEquals(150, this.calculator.add(50)); assertEquals(130, this.calculator.add(-20)); } @Test void testSub() { assertEquals(-100, this.calculator.sub(100)); assertEquals(-150, this.calculator.sub(50)); assertEquals(-130, this.calculator.sub(-20)); } }
在CalculatorTest
测试中,有两个标记为@BeforeEach
和@AfterEach
的方法,它们会在运行每个@Test
方法前后自动运行。
上面的测试代码在JUnit中运行顺序如下:
for (Method testMethod : findTestMethods(CalculatorTest.class)) { var test = new CalculatorTest(); // 创建Test实例 invokeBeforeEach(test); invokeTestMethod(test, testMethod); invokeAfterEach(test); }
可见,@BeforeEach
和@AfterEach
会“环绕”在每个@Test
方法前后。
还有一些资源初始化和清理可能更加繁琐,而且会耗费较长的时间,例如初始化数据库。JUnit还提供了@BeforeAll
和@AfterAll
,它们在运行所有@Test前后运行,顺序如下:
invokeBeforeAll(CalculatorTest.class);for (Method testMethod : findTestMethods(CalculatorTest.class)) { var test = new CalculatorTest(); // 创建Test实例 invokeBeforeEach(test); invokeTestMethod(test, testMethod); invokeAfterEach(test); } invokeAfterAll(CalculatorTest.class);
因为@BeforeAll
和@AfterAll
在所有@Test
方法运行前后仅运行一次,因此,它们只能初始化静态变量,例如:
public class DatabaseTest { static Database db; @BeforeAll public static void initDatabase() { db = createDb(...); } @AfterAll public static void dropDatabase() { ... } }
事实上,@BeforeAll
和@AfterAll
也只能标注在静态方法上。
因此,我们总结出编写Fixture的套路如下: