Android测试之旅之JUnit(二)

开始

通过Android测试之旅之JUnit(一)的学习,我们对JUnit的知识有了初步的认识。聪明的你是不是发现其实并没有你想象的那么难呢?这章我们继续来瞅瞅JUnit还有什么好玩的。今天我们用一个简单的例子给大家进行展示,方便更好的理解。

Parameterized

我们先来看下面一个待测试类PrettyTest:

public class PrettyTest {
    /** * 根据输入值的大小返回字符串 * @param a 输入值 * @return 返回的字符串结果 */
    public String print(int a){
        System.out.println("==========current input number is: " + a + "==========");
        return  a > 0? "大":"小";
    }
}

这个类相当的纯洁。如果你看过上一章的内容,机智的你一定可以很快的写出测试用例PrettyTest1:

public class PrettyTest1 {

    private static PrettyTest mTest;
    private static String expectedStrAbove;
    private static String expectedStrBelow;

    @BeforeClass
    public static void create(){
        mTest = new PrettyTest();
        expectedStrAbove = "大";
        expectedStrBelow = "小";
    }

    @Test
    public void testPrintAbove(){
        int i = 1;
        String resultStr = mTest.print(i);
        System.out.println("==========testPrintBelow result string is:"+ resultStr + "==========");
        Assert.assertEquals(expectedStrAbove,resultStr);
        Assert.assertNotEquals(expectedStrBelow,resultStr);
    }

    @Test
    public void testPrintBelow(){
        int i = -1;
        String resultStr = mTest.print(i);
        System.out.println("==========testPrintBelow result string is:"+ resultStr + "==========");
        Assert.assertEquals(expectedStrBelow,resultStr);
        Assert.assertNotEquals(expectedStrAbove,resultStr);
    }
}

仔细看看,很满意,很傲娇。但是,有个小问题,如果我们要用多个值来测试怎么办?这里只列举了大于0和小于0的两种情况。而在实际的开发中,一个方法中有的时候可能会有n多种情况。这个时候,Parameterized类的作用就能体现出来了。还是针对于PrettyTest,我么来看看PrettyTest2这个测试用例:

@RunWith(Parameterized.class)
public class PrettyTest2 {
    //-------初始化代码省略-------
    @Parameterized.Parameter
    public int testNum;

    @Parameterized.Parameters
    public static Collection<Integer> initData() {
        List<Integer> data = new ArrayList<>();
        data.add(-1);
        data.add(0);
        data.add(1);
        return data;
    }

    @Test
    public void test(){
        String resultStr = mTest.print(testNum);
        System.out.println("==========testPrintBelow result string is:"+ resultStr + "==========");
        if (testNum > 0){
            //-------判断是否与预期值相同-------
        }else{
            //-------判断是否与预期值相同-------
        }
    }
}

代码确实没有少多少,我们再来看下运行结果。
Android测试之旅之JUnit(二)_第1张图片
运行了三次,把我们在initData方法中设置的数据都跑了一遍。然后test方法中队各种情况进行相应的判断,是不是觉得这样效率提高了不少。其中@Parameter注解可以设置需要测试的公共变量,而@Parameters注解则是设置一个装满测试数据的集合。如果你不想使用@Parameter注解,还可以通过构造函数来设置。代码如下:

    private int testNum;
    public PrettyTest2(int num){
        this.testNum = num;
    }

测试结果和之前的测试一模一样。

Rules

JUnit

稍微回味一下,我们继续探索。Rules注解是个非常有意思注解,我们可以通过该注解在测试用例中模拟我们需要的行为,听这话有点昏昏的,别急,继续往下看。因为测试的场景有很多,JUnit为我们定义了很多非常有用的Rules。比如说TemporaryFolder这个类,这个类就是JUnit为我们提供在测试过程中创建文件夹的类,当测试结束之后,文件夹会自动删除。上代码:

public class RuleTester {

    private static File testFile = null;

    @Rule
    public TemporaryFolder mFolder = new TemporaryFolder();

    @Before
    public void before(){
        System.out.println("----------method before testFile is: "+ testFile + "----------");
        Assert.assertNull(testFile);
    }

    @Test
    public void test(){
        try {
            testFile = mFolder.newFile("myfile.txt");
            boolean flag = testFile.exists();
            System.out.println("----------method test testFile exists flag: "+ flag + "----------");
            Assert.assertTrue(flag);
        } catch (IOException e) {
            Assert.fail("exception is:"+e.getMessage());
        }
    }

    @AfterClass
    public static void after(){
        boolean flag = testFile.exists();
        System.out.println("----------method after testFile exists flag: "+ flag + "----------");
        Assert.assertFalse(flag);
    }
}

先看下显示结果。
Android测试之旅之JUnit(二)_第2张图片
事实证明确实在测试方法的过程中创建过一个文件,并且在测试用例结束的时候文件被删除了。是不是很神奇?很溜?当然,如果你有兴趣一步步的跟进去看源码,你就会发现这其实就是对一个文件创建,使用以及删除的一个过程。TemporaryFolder类继承了ExternalResource类,而ExternalResource类实现了接口TestRule中的apply方法。这里不贴代码是不是有点绕,别急,我们先继续往下走,走完再来回头看。

自定义

上面提到了TestRule这个神奇的接口,我打算写一个自带日志打印的Rule,里面包含一个打印消息的方法。在控制台可以看到Rule被调用的过程。好,开始行动!

public class LogRule implements TestRule{

    private Statement mBase;

    @Override
    public Statement apply(Statement base, Description description) {
        this.mBase = base;
        return new LogStatement(base);
    }

    public void print(String message){
        System.out.println("LogRule message is:" + message);
    }

    public class LogStatement extends Statement{

        private final Statement base;

        public LogStatement(Statement base) {
            this.base = base;
        }

        @Override
        public void evaluate() throws Throwable {
            System.out.println("method evaluate before");
            try{
                base.evaluate();
            }finally {
                System.out.println("method evaluate after");
            }
        }
    }
}

在这个自定义的Rule中,我们重写了apply方法,添加了print方法,并且重写了Statement类中的evaluate方法,在这个方法前后我们加了日志的打印。下面来看下在测试用例中的调用和输出结果。

public class LogRuleTest {

    @Rule
    public LogRule mLogRule= new LogRule();

    @Test
    public void test(){
        String message = "method test";
        mLogRule.print(message);
    }
}

Android测试之旅之JUnit(二)_第3张图片
调用是不是很方便,现在在回头看看之前我们讲到的文件创建,使用和删除的过程,是不是一下子就恍然大悟了。那句俗话怎么说的,车到山前必有路,船到桥头自然直。

Categories

看到这里有点累了吧,别急,快结束了。在上篇我们介绍过了SuiteClasses来选择需要测试的测试用例。而Categories这个注解则可以帮你更上一层楼。为什么这么说呢,因为这个注解可以给你测试用例中的测试方法进行归类。闲话不多说,上代码!

public class ICategories {

    public interface First {
    }

    public interface Second {
    }
}
public class CategoriesA {
    @Test
    public void a() {
        System.out.println("------class CategoriesA method a called------");
    }

    @Category(ICategories.First.class)
    @Test
    public void b() {
        System.out.println("------class CategoriesA method b called------");
    }
}
@Category({ ICategories.First.class, ICategories.Second.class })
public class CategoriesB {
    @Test
    public void c() {
        System.out.println("------class CategoriesB method c called------");
    }
}

有三个文件,希望大家不要看晕了。我们首先定义两种测试的类型,分别是接口First和Second。然后通过注解@Category对不同测试用例中的不同方法进行标注,最后选择自己需要测试的内容。其中测试类CategoriesAa方法没有任何标志,b方法注明了First接口。CategoriesB测试用例上注明了FirstSecond两个接口。我们接着往下看:

@RunWith(Categories.class)
@Categories.IncludeCategory(ICategories.First.class)
@Categories.ExcludeCategory(ICategories.Second.class)
@Suite.SuiteClasses({ CategoriesA.class, CategoriesB.class })
public class CategoriesTest {

}

我们运行CategoriesTest这个测试用例。结果如下:
Android测试之旅之JUnit(二)_第4张图片
仅仅运行了CategoriesAb方法,想必聪明的你已经知道了,@IncludeCategory注解表示我们需要包含哪个接口,@ExcludeCategory注解则表示我们剔除哪些接口。所以包含First接口而不包含Second接口的方法只有CategoriesAb方法。好啦!你可以边回味今天的知识边休息啦。

结尾

到这里,我们Android测试之旅之JUnit的全部内容就已经结束啦。不知道这些内容是否可以帮助您解决问题。当然,有什么问题可以随时留言,我一定会积极回答的。

你可能感兴趣的:(android,JUnit,测试)