浅谈Android单元测试(1)——Junit和Jacoco开发中的应用

  • 前言

        单元测试的优点:1.开发中单元测试能帮助开发人员编写代码、提升质量、减少bug。2.编写单元测试代码的过程就是促使开发人员思考工作代码实现内容和逻辑的过程,之后实现工作代码的时候,开发人员思路会更清晰,实现代码的质量也会有相应的提升。3.让代码维护更容易。由于给代码写很多单元测试,相当于给代码加上了规格说明书,开发人员通过读单元测试代码也能够帮助开发人员理解现有代码。4.单元测试会让你注意到方法内部实现细节,让你思考,有助于改进代码质量和设计。

        单元测试的缺点:1.单元测试的学习成本比较高。2.编写单元测试会增加程序员工作量。3.推广和运用单元测试需要比较大的投入。但跟单元测试的优点比较起来,为了克服和解决这些缺点所在的付出是值得的。
  • Junit简单实用

        现在Android开发者基本上都使用as开发了,我的as版本是3.0.1,as在新建一个项目就自动导入了Junit4.12,在src下面自动创建了test文件夹,文件夹中自动创建ExampleUnitTest类,并已经有个简单的案例。单击测试方法左侧的箭头就可以运行,运行通过后左侧箭头会变成圆圈箭头,如图:


        上图就是简单的junit单元测试,方法上@test注解注意一下,每个测试方法都必须有这个注解才能运行。测试方法里面assertEquals是判断两个值是否相等,如果预期值与真实值相等,则运行success,反之Failure。

  • 验证方法

        上面提到的assertEquals就是验证方法,JUnit为我们提供的assert方法,多数都在Assert这个类里面。最常用的方法如下:

    assertEquals(double expected, double actual) 验证两个值是否相等,相等则通过,反之失败

    assertEquals(double expected, double actual, double delta) 比较两个数是否相等,偏差在tolerance范围内。

    assertTrue(boolean condition) 验证contidion的值是true

    assertFalse(boolean condition) 验证contidion的值是false

    assertNull(java.lang.Object object) 验证object的值是null

    assertNotNull(java.lang.Object object) 验证obj的值不是null

    assertSame(java.lang.Object expected, java.lang.Object actual) 验证expected和actual是同一个对象

    assertNotSame(java.lang.Object unexpected, java.lang.Object actual) 验证expected和actual不是同一个对象

    fail() 让测试方法失败

        注意:上面的每一个方法,都有一个重载的方法,可以在前面加一个String类型的参数,表示如果验证失败的话,将用这个字符串作为失败的结果消息。 

  • 常用的注解

        @Test 注解的public void方法将会被当做测试用例

        @Before 在@Test注解方法执行之前执行,一般做初始化资源和创建对象用。

        @After 注解的public void方法会使该方法在@Test注解方法执行后被执行,一般做释放资源和销毁对象用。

        @BeforeClass  注解一个public static void 方法,并且该方法不带任何参数,会使该方法在所有测试方法被执行前执行一次,并且只执行一次

         @AfterClass 注解一个public static void方法会使该方法在测试类中的所有测试方法执行完后被执行

        @Ignore 对包含测试类的类或@Test注解方法使用@Ignore注解将使被注解的类或方法不会被当做测试执行

    小结:@Before和@After是每执行一次@Test方法都调用一次,@BeforeClass 和@AfterClass是在跑一个测试类所有测试方法之前执行一次,只执行一次,注意:@BeforeClass 和@AfterClass修饰的方法必须是静态的。上面都是常用的注解,下面我们看一下怎么测试我们自己的代码。

  • as快速创建测试用例

    在你要测试的类中右击类名或者方法名字->Go To->Test->Create New Test->选择你要测试的方法和是否添加@Before、@After方法->Ok->选择路径->创建完成。注意:只能选择测试public修饰的方法。如图所示:


    你就会创建类名为"原类名+test"的测试类,剩下就需要编写代码了,在@Before中创建对象,在@Test中调用你想测试的方法,代码如下:

public class JunitTestDemo {

    /**
     * 非空判断
     * @param name
     * @param password
     * @return
     */
    public boolean checkInput(String name,String password){
        if (TextUtils.isEmpty(name)||TextUtils.isEmpty(password)) {
            return false;
        }
        return true;
    }
}

单元测试类

public class JunitTestDemoTest {

    public JunitTestDemo junitTestDemo;
    @Before
    public void setUp() throws Exception {
        junitTestDemo = new JunitTestDemo();//创建对象
    }
    @Test
    public void checkInput() throws Exception {
        boolean flag = junitTestDemo.checkInput("zhangjianan", "123456");//获取运行结果
        Assert.assertEquals(flag,true);//校验运行结果
    }
    @After
    public void tearDown() throws Exception {
    }

}
        运行测试方法会报一个错:“java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked”,因为TextUtils是Android.jar包中的,Junit无法访问,的这里需要我们自己写TextUtils。类名和路径要与代码中保持一致。如图:

        这是有返回值的单元测试,没有返回值的单元测试就涉及到mock概念了,需要用到Mockito框架了。

  • Jacoco统计单元测试覆盖率

        查看整个项目覆盖率需要配置gradle,具体代码:

apply plugin: 'com.android.application'
apply plugin: 'jacoco'
android {
    ...
    buildTypes {
        debug {
            testCoverageEnabled = true
        }
        release {
           ...
        }
    }
}
dependencies {
   ...
}
jacoco {
    toolVersion = "0.7.1.201405082137"
}
//这里定义为你想要进行代码覆盖率统计的java目录
def coverageSourceDirs = ['src/main/java']
task jacocoTestReport(type: JacocoReport, dependsOn: "testDebugUnitTest") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"
	//这里为编译后产生的.class文件目录,excludes为你不想进行代码覆盖率统计的文件
    classDirectories = fileTree(dir: '../app/build/intermediates/classes/debug',
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*'])
    additionalSourceDirs = files(coverageSourceDirs)
    sourceDirectories = files(coverageSourceDirs)
	//代码覆盖率报告的生成文件目录
    executionData = files('../app/build/jacoco/testDebugUnitTest.exec')
	//这里为生成报告的类型
    reports {
        xml.enabled = true
        html.enabled = true
    }
}
        控制台输入gradlew jacocoTestReport命令或点击as右侧gradle->androidtest->app->tasks->reporting->双击jacocoTestReport就会生成覆盖率文档。如下图:


        运行jacocoTestReport成功后,可以到app->build->reports->jacoco->jacocoTestReport->html文件夹中的index.html查看结果,如图:


        从上图看到整个项目单元测试覆盖率48%,因为会统计未忽略的所有类的单元测试覆盖率,Activity也被算入,但我们并没有对Activity做单元测试,所以Acitity的覆盖率为0%,我们只对JunitTestDemo类做了单元测试,JunitTestDemo类的覆盖率也没有达到100%。原因是我们只做了正例的单元测试,没有做反例单元测试,如上图所示,所以return false就没有被覆盖到,这部分很简单,就不继续做了。

        上面介绍的都是有返回结果的测试方式,如果没有返回结果该怎么测试呢,这我们就得使用Mockito。请看下一篇。

  • 下篇预告

        下一篇会学习Mockito的简单应用。很荣幸我们能一起学习,一起成长。如果文中有错误,欢迎大神留言指正。谢谢!

---------------github源码地址---------------------    

想要跟我一起学习一起成长,请关注我的公众号:程序员持续发展方案


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值