安卓单元测试(八):Junit Rule的使用

JUnit Rule是什么

一个JUnit Rule就是一个实现了TestRule的类,这些类的做用相似于@Before@After,是用来在每一个测试方法的执行先后执行一些代码的一个方法。 html

若是你不清楚@Before@After这些Annotation的意思,Chances are你还不了解Junit的使用,建议先看这篇文章java

那为何不直接用这些annotation呢?这是由于它们都只能做用于一个类,若是同一个setup须要在两个类里面同时使用,那么你就要在两个测试类里面定义相同的@Before方法,而后里面写相同的代码,这就形成了代码重复。有的人说你能够用继承啊,首先我想说,我很讨厌继承这个东西,因此若是能够不用继承的话,我就不会用;再次我想说,若是你不讨厌继承的话,从如今开始,你也应该慢慢的讨厌它了。 android

此外,JUnit Rule还能作一些@Before这些Annotation作不到的事情,那就是他们能够动态的获取将要运行的测试类、测试方法的信息。这个在接下来的一个例子里面能够看到。git

怎么用JUnit Rule?

使用框架自带的Rule

不少测试框架好比JUnit、Mockito自带给咱们不少已经实现过好了的JUnit Rule,咱们能够直接拿来用。好比TimeoutTemporaryFolder,等等。这些Rule的使用方法很是简单。定义一个这些类的public field,而后用@Rule修饰一下就行了。好比github

public class ExampleTest {
    @Rule
    public Timeout timeout = new Timeout(1000);  //使用Timeout这个 Rule,

    @Test
    public void testMethod1() throws Exception {
        //your tests
    }
    
    @Test
    public void testMethod2() throws Exception {
        //your tests2
    }

    //other test methods
}

那么,对于上面这个ExampleTest的每个测试方法。它们的运行时间都不能超过1秒钟,否则就会标志为失败。而它的实现方式就是在每一个方法测试以前都会记录一下时间戳,而后开始倒计时,1秒钟以后若是方法尚未运行结束,就把结果标记为失败。 app

这里须要注意的一点是Rule须要是public field。框架

实现本身的Rule

固然,若是只能用框架自带的Rule,那功能未免太受限了,JUnit Rule最强大的地方在于,咱们能够本身写知足咱们本身须要的Rule。因此如今的问题是怎么写这个Rule。简单来讲,写一个Rule就是implement一个TestRule interface,实现一个叫apply()的方法。这个方法须要返回一个Statement对象。下面给一个例子,这个 Rule的做用是,在测试方法运行以前,记录测试方法所在的类名和方法名,而后在测试方法运行以后打印出来,至于怎么在测试方法运行先后作这些事情,下面例子中的注释里面说的很清楚。jvm

public class MethodNameExample implements TestRule {
    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //想要在测试方法运行以前作一些事情,就在base.evaluate()以前作
                String className = description.getClassName();
                String methodName = description.getMethodName();
                
                base.evaluate();  //这其实就是运行测试方法
                
                //想要在测试方法运行以后作一些事情,就在base.evaluate()以后作
                System.out.println("Class name: "+className +", method name: "+methodName);
            }
        };
    }
}

这个Rule这样就算写好了,如今来试试,用这个 Rule的方法跟使用自带的Rule的用法是同样的,写一个public field,用@Rule修饰一下就行了。ide

public class ExampleUnitTest {
    @Rule
    public MethodNameExample methodNameExample = new MethodNameExample();
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }

    @Test
    public void mulitiplication_isCorrect() throws Exception {
        assertEquals(4, 2 * 2);
    }
}

运行结果以下:单元测试

在右边的框框能够看到,把测试方法的方法名和所在的类名打印出来了。

上面的例子对于TestRule的实现应该说的比较清楚,可是看起来没多大用。下面给另外的一个例子,你们或许会以为这个东西更有用一点。在安卓里面,咱们常常在不少地方须要用到Context这个东西。咱们的作法是将这个东西保存在一个类里面,做为静态变量存在:

public class ContextHolder {
    private static Context sContext;

    public static void set(Context context) {
        sContext = context;
    }

    public static Context get() {
        return sContext;
    }
}

而后在自定义的Application#onCreate()里面调一下ContextHolder.set()将这个context初始化。

若是你的当前项目是一个library,那么你在测试环境下是没有这个Application的,Robolectric会给你造一个Application,放在RuntimeEnvironment.application里面。因此在测试环境下你可使用这个instance来将ContextHolder初始化。在这种状况下,你就能够用一个Rule来实现这样的效果,在用到Context的测试方法运行以前将ContextHolder初始化:

public class ContextRule implements TestRule {
    @Override
    public Statement apply(Statement base, Description description) {
        ContextHolder.set(RuntimeEnvironment.application);
        return base;
    }
}

这样,在运行一些用到context的测试方法以前,你就可使用这个Rule来给Context赋值了。

其实,最经常使用的Rule之一就是结合@Mock之类的Annotation快速的建立mock,可是这点我想做为一篇单独的文章写一下。缘由之一是由于它涉及到的东西不只仅是Rule,还有其它的一些东西。更重要的缘由是,我但愿你们能知道这个东西,而不是被这篇文章淹没。请关注下一篇文章吧!

小结

JUnit Rule的介绍就到这里,应该说比较简单,倒是很是有帮助。但愿这篇文章能帮助到你们了解这个东西。

这篇文章的代码放在这个github repo里面。

最后,若是你对安卓单元测试感兴趣,欢迎加入咱们的交流群,由于群成员超过100人,没办法扫码加入,请关注下方公众号获取加入方法。

相关文章
相关标签/搜索