阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

使用Fixture

28次阅读
没有评论

共计 2247 个字符,预计需要花费 6 分钟才能阅读完成。

在一个单元测试中,我们经常编写多个 @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 的套路如下:

  1. 对于实例变量,在 @BeforeEach 中初始化,在 @AfterEach 中清理,它们在各个 @Test 方法中互不影响,因为是不同的实例;
  2. 对于静态变量,在 @BeforeAll 中初始化,在 @AfterAll 中清理,它们在各个 @Test 方法中均是唯一实例,会影响各个 @Test 方法。

大多数情况下,使用 @BeforeEach@AfterEach就足够了。只有某些测试资源初始化耗费时间太长,以至于我们不得不尽量“复用”时才会用到 @BeforeAll@AfterAll

最后,注意到每次运行一个 @Test 方法前,JUnit 首先创建一个 XxxTest 实例,因此,每个 @Test 方法内部的成员变量都是独立的,不能也无法把成员变量的状态从一个 @Test 方法带到另一个 @Test 方法。

练习

使用 Fixture 编写单元测试。

下载练习

小结

编写 Fixture 是指针对每个 @Test 方法,编写 @BeforeEach 方法用于初始化测试资源,编写 @AfterEach 用于清理测试资源;

必要时,可以编写 @BeforeAll@AfterAll,使用静态变量来初始化耗时的资源,并且在所有 @Test 方法的运行前后仅执行一次。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计2247字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中