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{
            //-------判断是否与预期值相同-------
        }
    }
}

代码确实没有少多少,我们再来看下运行结果。

运行了三次,把我们在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);
    }
}

先看下显示结果。

事实证明确实在测试方法的过程中创建过一个文件,并且在测试用例结束的时候文件被删除了。是不是很神奇?很溜?当然,如果你有兴趣一步步的跟进去看源码,你就会发现这其实就是对一个文件创建,使用以及删除的一个过程。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);
    }
}

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

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这个测试用例。结果如下:

仅仅运行了CategoriesAb方法,想必聪明的你已经知道了,@IncludeCategory注解表示我们需要包含哪个接口,@ExcludeCategory注解则表示我们剔除哪些接口。所以包含First接口而不包含Second接口的方法只有CategoriesAb方法。好啦!你可以边回味今天的知识边休息啦。

结尾



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

时间: 2024-10-11 03:41:37

Android测试之旅之JUnit(二)的相关文章

Android中如何使用JUnit进行单元测试 eclipse

Android中如何使用JUnit进行单元测试 在我们日常开发android app的时候,需要不断地进行测试,所以使用JUnit测试框架显得格外重要,学会JUnit可以加快应用的开发周期. Android中建立JUnit测试环境有以下两种方法. 一.直接在需要被测试的工程中新建测试类 集成步骤: 1.在androidManifest.xml文件中添加以下代码: <instrumentation android:name="android.test.InstrumentationTestR

Android系列之Fragment(二)Fragment的生命周期和返回栈

Android系列之Fragment(二)Fragment的生命周期和返回栈 - Android - 次元立方网 - 电脑知识与技术互动交流平台 [正文] 上一章节中(Android系列之Fragment(一)----Fragment加载到Activity当中),我们对Fragment的生命周期进行了简单介绍,这一章节将对生命周期和返回栈进行详细介绍. 一.Fragment的生命周期初探: 因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activ

从零开始学android&lt;android事件的处理方式.二十四.&gt;

在android中一共有 多种事件,每种事件都有自己相对应的处理机制 如以下几种 1 单击事件 View.OnClickListener public abstract void onClick (View v) 单击组件时触发 2 单击事件 View.OnLongClickListener public abstract boolean onLongClick (View v) 长按组件时触发 3 键盘事件 View.OnKeyListener public abstract boolean

Android Day02-Android中单元测试(junit测试)&monkey测试

Android中junit测试有2种实现方式 第1种:一般Android工程的实现方式 1.在清单文件中添加2项内容 首先在AndroidManifest.xml中加入下面红色代码: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.itcast.action" android:versionCode="1"  android:v

密西西比河谷州立大学:Android应用程序开发(二)

第二讲 Hello World 密西西比河谷州立大学:Android应用程序开发(二),码迷,mamicode.com

Android Touch系统简介(二):实例详解onInterceptTouchEvent与onTouchEvent的调用过程

上一篇文章主要讲述了Android的TouchEvent的分发过程,其中有两个重要的函数:onInterceptTouchEvent和onTouchEvent,这两个函数可被重装以完成特定的逻辑.onInterceptTouchEvent的定义为于ViewGroup中,默认返回值为false,表示不拦截TouchEvent.onTouchEvent的定义位于View中,当ViewGroup要调用onTouchEvent时,会利用super.onTouchEvent.ViewGroup调用onTo

Android短信监听(二)——利用ContentObserver实现短信监听

MainActivity如下: package cc.testsmslistener; import cc.testsmslistener.SMSContentObserver.MessageListener; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.widget.TextView; import android.app.Activity; /** *

SQL Server 服务器磁盘测试之SQLIO篇(二)

上次放出了一篇文章,针对磁盘卷簇大小默认4KB和自定义64KB进行了测试,测试内容为随机和顺序读写,大小为8KB和64KB,有人觉得这并没有照顾到SQL Server所有的IO使用情景.这篇测试文章,我们就来尽可能模拟一下SQL Server IO的行为,全方位对簇大小4KB.8KB和64KB做一次验证,注意:本次我们增加了簇为8KB的大小. 重点说明:本测试使用的是两块SSD组成的RAID1 首先,我们先来分析SQL Server的IO行为,参考网址:Choosing what SQLIO t

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte