扩展 JUnit 4,使用定制的 Runner
JUnit 的测试用例总是由 Runner 去执行,JUnit 提供了 @RunWith 这个测试类的 Annotation, 可来指定自定义的 Runner。如果未指定特别的 Runner,那么会采用默认的 Runner,可能不同的环境,如 Eclipse,控制台下会有不同的默认 Runner。
如果不清楚 Runner 是什么,那么可能见过 @RunWith(SpringJUnit4ClassRunner.class) 这个东西,它有助你加载 Spring 的配置文件,及与 Spring 相关的事物。
那么自定义的 Runner 有什么用呢?它可以截获到 @BeforeClass, @AfterClass, @Before, @After 这些事件,也就是能在测试类开始和结束执行前后,每个测试方法的执行前后处理点事情。
比如说从外部读取内容进行初始化测试数据,而且 JUnit 本身就提供了 @RunWith(Parameterized.class) 这个参数化 Runner,用了为带参数测试方法循环填充数据进行测试。JUnit 的参数化测试比 C# 还是要笨拙一些,C# 直接用方法注解一行行设置参数,我想 JUnit 稍加定制的话也行的。
现在用个例子来说明定制化 Runner 的表现功力,需继承自 BlockJUnit4ClassRunner, 这又是继承自 ParentRunner。可自定义那些 with 开头的方法来截获相应的事件,如:
来自 ParentRunner 的 withBeforeClasses, withAfterClasses
和定义的 BlockJUnit4ClassRunner 中的 withBefores, withAfters, withPotentialTimeout
等等,总之祖先类中的 protected 方法一般就是给大家开的口,看例子:
上面的 junitStatement.evaluate() 才是真正去执行相应的用 @Before, @After, @BeforeClass, @AfterClass 和 @Test 标的方法,所以可以在执行真正方法前面植入点什么。
看测试类:
看执行效果吧:
Before Class: cc.unmi.MyTest
execute beforeClass
After method: should_return_something_if_age_equals_18
Before before method: should_return_something_if_age_equals_18
execute before
execute should_return_something_if_age_equals_18
After before method: should_return_something_if_age_equals_18
execute after
execute afterClass
After Class: cc.unmi.MyTest
由于给出了的执行结果,所以就不多解释了。
应用场景探讨:因为在 “拦截” 方法中可以感知道当前执行类和方法,所以可以在 withBeforeXxx 时初始化测试数据,比如可以加入自定义的注解,根据注解,继续用反射的方式对类、实例变量进行初始化,或某些清理工作。
再学习下 JUnit 的 @RunWith(Parameterized.class) 参数化测试。除此之外,还有 @ClassRule 和 @Rule 也有异曲同功之妙,见 JUnit扩展方式(一)-使用Rule对JUnit进行扩展(JUnit4.10) 。
参考: 1. JUnit扩展方式(二)-使用Runner对JUnit进行扩展(基础)大
2. JUnit中的测试套件和参数化测试
3. 使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试
4. 使用RunWith注解改变JUnit的默认执行类,并实现自已的Listener
5. JUnit扩展方式(一)-使用Rule对JUnit进行扩展(JUnit4.10) 永久链接 https://yanbin.blog/extend-junit-4-customized-runner/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
如果不清楚 Runner 是什么,那么可能见过 @RunWith(SpringJUnit4ClassRunner.class) 这个东西,它有助你加载 Spring 的配置文件,及与 Spring 相关的事物。
那么自定义的 Runner 有什么用呢?它可以截获到 @BeforeClass, @AfterClass, @Before, @After 这些事件,也就是能在测试类开始和结束执行前后,每个测试方法的执行前后处理点事情。
比如说从外部读取内容进行初始化测试数据,而且 JUnit 本身就提供了 @RunWith(Parameterized.class) 这个参数化 Runner,用了为带参数测试方法循环填充数据进行测试。JUnit 的参数化测试比 C# 还是要笨拙一些,C# 直接用方法注解一行行设置参数,我想 JUnit 稍加定制的话也行的。
现在用个例子来说明定制化 Runner 的表现功力,需继承自 BlockJUnit4ClassRunner, 这又是继承自 ParentRunner。可自定义那些 with 开头的方法来截获相应的事件,如:
来自 ParentRunner 的 withBeforeClasses, withAfterClasses
和定义的 BlockJUnit4ClassRunner 中的 withBefores, withAfters, withPotentialTimeout
等等,总之祖先类中的 protected 方法一般就是给大家开的口,看例子:
1package cc.unmi;
2
3import org.junit.runners.BlockJUnit4ClassRunner;
4import org.junit.runners.model.FrameworkMethod;
5import org.junit.runners.model.InitializationError;
6import org.junit.runners.model.Statement;
7
8/**
9* @author Unmi
10* @created 2013-04-17
11*/
12@SuppressWarnings("deprecation")
13public class MyTestRunner extends BlockJUnit4ClassRunner {
14
15 private Class<?> clazz;
16
17 public MyTestRunner(Class<?> klass) throws InitializationError {
18 super(klass);
19 this.clazz = klass;
20 }
21
22 // 拦截 BeforeClass 事件
23 protected Statement withBeforeClasses(final Statement statement) {
24 final Statement junitStatement = super.withBeforeClasses(statement);
25 return new Statement() {
26 @Override
27 public void evaluate() throws Throwable {
28 System.out.println("Before Class: " + clazz.getName());
29 junitStatement.evaluate();
30 }
31
32 };
33 }
34
35 // 拦截每一个方法的 Before 事件
36 protected Statement withBefores(final FrameworkMethod method, Object target, final Statement statement) {
37
38 final Statement junitStatement = super.withBefores(method, target, statement);
39 return new Statement() {
40 @Override
41 public void evaluate() throws Throwable {
42 System.out.println("Before before method: " + method.getName());
43 junitStatement.evaluate();
44 System.out.println("After before method: " + method.getName());
45 }
46 };
47 }
48
49 // 截获每一个测试方法的 after 事件
50 protected Statement withAfters(final FrameworkMethod method, Object target, final Statement statement) {
51 final Statement junitStatement = super.withAfters(method, target, statement);
52 return new Statement() {
53 @Override
54 public void evaluate() throws Throwable {
55 System.out.println("After method: " + method.getName());
56 junitStatement.evaluate();
57 }
58
59 };
60 }
61
62 // 截获测试类的 after 事件
63 protected Statement withAfterClasses(final Statement statement) {
64 final Statement junitStatement = super.withAfterClasses(statement);
65 return new Statement() {
66 @Override
67 public void evaluate() throws Throwable {
68 junitStatement.evaluate();
69 System.out.println("After Class: " + clazz.getName());
70 }
71 };
72 }
73}上面的 junitStatement.evaluate() 才是真正去执行相应的用 @Before, @After, @BeforeClass, @AfterClass 和 @Test 标的方法,所以可以在执行真正方法前面植入点什么。
看测试类:
1package cc.unmi;
2
3import org.junit.After;
4import org.junit.AfterClass;
5import org.junit.Before;
6import org.junit.BeforeClass;
7import org.junit.Test;
8import org.junit.runner.RunWith;
9
10/**
11* @author Unmi
12* @created 2013-04-17
13*/
14@RunWith(MyTestRunner.class)
15public class MyTest {
16
17 @BeforeClass
18 public static void beforeClass() {
19 System.out.println("execute beforeClass");
20 }
21
22 @Before
23 public void before() {
24 System.out.println("execute before");
25 }
26
27 @Test
28 public void should_return_something_if_age_equals_18() {
29 System.out.println("execute should_return_something_if_age_equals_18");
30 }
31
32 @After
33 public void after() {
34 System.out.println("execute after");
35 }
36
37 @AfterClass
38 public static void afterClass() {
39 System.out.println("execute afterClass");
40 }
41 }看执行效果吧:
Before Class: cc.unmi.MyTest
execute beforeClass
After method: should_return_something_if_age_equals_18
Before before method: should_return_something_if_age_equals_18
execute before
execute should_return_something_if_age_equals_18
After before method: should_return_something_if_age_equals_18
execute after
execute afterClass
After Class: cc.unmi.MyTest
由于给出了的执行结果,所以就不多解释了。
应用场景探讨:因为在 “拦截” 方法中可以感知道当前执行类和方法,所以可以在 withBeforeXxx 时初始化测试数据,比如可以加入自定义的注解,根据注解,继续用反射的方式对类、实例变量进行初始化,或某些清理工作。
再学习下 JUnit 的 @RunWith(Parameterized.class) 参数化测试。除此之外,还有 @ClassRule 和 @Rule 也有异曲同功之妙,见 JUnit扩展方式(一)-使用Rule对JUnit进行扩展(JUnit4.10) 。
参考: 1. JUnit扩展方式(二)-使用Runner对JUnit进行扩展(基础)大
2. JUnit中的测试套件和参数化测试
3. 使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试
4. 使用RunWith注解改变JUnit的默认执行类,并实现自已的Listener
5. JUnit扩展方式(一)-使用Rule对JUnit进行扩展(JUnit4.10) 永久链接 https://yanbin.blog/extend-junit-4-customized-runner/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。