本文简要介绍JUnit5中的依赖注入特性。在介绍之前,先以斐波那契数列为例,看看JUnit4的参数化测试。
package com.github.junit5.parameter;
public class Fibonacci {
public int compute(int n) {
int result = 0;
if (n <= 1) {
result = n;
} else {
result = compute(n - 1) + compute(n - 2);
}
return result;
}
}
如果用JUnit4编写参数化测试用例,可以有如下的实现,
package com.github.junit5.parameter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertEquals;
import static org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FibonacciJunit4Test {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
//@Parameter(0) //通过成员方法注入
private int fInput;
//@Parameter(1)
private int fExpected;
private Fibonacci fibonacci= new Fibonacci();
public FibonacciJunit4Test(int input, int expected) {
fInput= input;
fExpected= expected;
}
@Test
public void testFibonacci() {
assertEquals(fExpected, fibonacci.compute(fInput));
}
}
从上述用例中可以了解到,
l @RunWith (Parameterized.class)指定运行器。在JUnit4中,JUnit将根据用例类中的@RunWith注解所指明的运行器(runner)来运行测试。当在用例类上面指定@RunWith (Parameterized.class)的运行器时,就可以实现参数化测试。
l @Parameters指定提供测试数据集的方法。JUnit4通过在一个静态方法上加上一个@Parameters注解,并且返回一个集合的方式来指定参数化测试所需的数据集。
l 参数注入,由于JUnit4是通过 @RunWith(Parameterized.class)来提供不同测试类的实例来实现参数化测试,因此参数可以通过测试类的带参构造方法来实现注入,或者是在测试类的公有成员参数上通过@Parameter(#)来实现,其中#代表了入参数组的下标序号(从0开始)
l 无参的测试方法。JUnit4中,@Test必须注解在无参的方法上。
可以看到,在JUnit4中为了实现参数化测试,还是比较繁琐的。而这些繁琐的背后的根本原因,其实仅仅是因为JUnit团队自身的一个约定,那就是测试方法必须是无参的。
而在Junit5中,Junit团队不再自我设限,允许JUnit5的测试方法可以带有参数,并且可以借助这个功能注入TestInfo、TestReport等测试上下文。也借助于此功能,Junit5中重新设计的参数化测试解决方案。来看一下如果使用新的方案来实现斐波那契数列测试。
package com.github.junit5.parameter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.assertj.core.api.Assertions.assertThat;
public class FibonacciTest {
private Fibonacci fibonacci= new Fibonacci();
@ParameterizedTest
@CsvSource({ "0, 0 ", "1, 1", "2, 1", "3, 2", "4, 3", "5, 5" , "6, 8"})
public void testFibonacci(int fInput, int fExpected) {
assertThat(fibonacci.compute(fInput)).isEqualTo(fExpected);
}
}
无论从代码的篇幅上,还是用例编写的流畅程度上来看,利用这个Junit5的新功能,让参数化测试写起来更为舒服了。
要利用JUnit5的依赖注入和参数能力,在运行时动态解析参数,让测试构造函数或@Test, @BeforeEach, @AfterEach, @BeforeAll或@AfterAll方法接受参数,就得在扩展模型中实现并注册ParameterResolver这个接口。
再来看一个Hoverfly的案例。Hoverfly是HTTP的服务模拟器,可以录制和回放HTTP请求。以下是Hoverfly针对JUnit5的扩展,用于在Junit5中使用Hoverfly。
public class HoverflyExtension implements AfterEachCallback, BeforeEachCallback,
AfterAllCallback, BeforeAllCallback, ParameterResolver {}
其中就实现了ParameterResolver接口。利用JUnit5提供的注入功能,可以在测试用例中注入Hoverfly,然后利用它的API来编写并提供mock服务。
@ExtendWith(HoverflyExtension.class)
class SimulationTests {
@Test
void shouldDoSomethingWith(Hoverfly hoverfly) {
hoverfly.simulate(dsl(
service("www.my-test.com")
.post("/api/bookings").body("{\"flightId\": \"1\"}")
.willReturn(created("http://localhost/api/bookings/1"))));
// ...
}
}