• Jenkins构建Android项目持续集成之单元测试及代码覆盖率


    单元测试

      在软件开发中一直在推崇TDD(测试驱动开发),但是一直不能被有效的执行或者并不是真正的测试驱动开发(先开发后写单元测试),因为我们懒!而Android开发又是大多应用层面的开发,很多都是和视图层紧密相连的,业务逻辑和view相绑定,这导致编写单元测试有相当大的困难,因此就我项目而言,只针对工具类、服务端API编写单元测试。关于android Studio如何编写单元测试并运行,可以看之前写的一篇文章Android Studio 单元测试

    代码覆盖率

      编写好单元测试后,我们需要知道,测试用例是否覆盖了代码的所有分支情况,这样才能保证代码的可靠性、正确性。

    编写测试用例

    如果使用Android Studio创建项目的话,那么默认的会在androidTest包下生成一个ApplicationTest类,在这里面可以写测试用例。

    测试用例

    上图写的是一个SharedPreferences Util工具类的测试用例。 
    如果你的测试用例不想写在这个包下,想自定义,也可以在项目的build.gradle写如下的配置

    android {
        sourceSets {
            androidTest{
                java.srcDirs = ['src/com/helen/andbase/tests']
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Gradle配置jacoco

      Jacoco是一个开源的覆盖率工具。这里讲下gradle如何配置。 
      首先要在项目的build.gradle引入插件,语句如下:

     apply plugin: 'jacoco'
    • 1
    • 1

      然后注明使用的版本号

    jacoco{
            version "0.7.4.201502262128"
        }
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

      接着,申明一个gradle task

    task jacocoTestReport(type:JacocoReport,dependsOn:"connectedAndroidTest"){
        group = "Reporting"
        description = "Generate Jacoco coverage reports after running tests."
        reports{
            xml.enabled = false
            html.enabled = true
            csv.enabled = false
        }
        classDirectories = fileTree(
                dir : "$buildDir/intermediates/classes/debug",
                excludes : [
                        '**/*Test.class',
                        '**/R.class',
                        '**/R$*.class',
                        '**/BuildConfig.*',
                        '**/Manifest*.*'
                ]
        )
        def coverageSourceDirs = ['src/main/java']
        additionalSourceDirs = files(coverageSourceDirs)
        sourceDirectories = files(coverageSourceDirs)
        additionalClassDirs = files(coverageSourceDirs)
        executionData = files("$buildDir/outputs/code-coverage/connected/coverage.ec")
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

      最后,打开testCoverageEnabled,需要注意的是,打开该属性的话,在断点调试的时候会导致方法参数值丢失(看不到),所以在调试的时候要记得把它关掉。

    buildTypes {
            debug{
                testCoverageEnabled true
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

      完整的gradle配置如下

    apply plugin: 'com.android.library'
    //代码覆盖率插件
    apply plugin: 'jacoco'
    
    android {
        compileSdkVersion 22
        buildToolsVersion '22.0.1'
        defaultConfig {
            minSdkVersion 8
            targetSdkVersion 22
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            debug{
                testCoverageEnabled true
            }
        }
        lintOptions {
            abortOnError false
        }
        packagingOptions {
            exclude 'META-INF/NOTICE'
            exclude 'META-INF/LICENSE'
        }
        jacoco{
            version "0.7.4.201502262128"
        }
    }
    //jacocoTestReport依赖于connectedAndroidTest task,所以在执行jacoco之前需要先执行connectedAndroidTest,也就是说需要连接测试机(模拟器or真机)
    task jacocoTestReport(type:JacocoReport,dependsOn:"connectedAndroidTest"){
        group = "Reporting"
        description = "Generate Jacoco coverage reports after running tests."
        reports{
            xml.enabled = false
            html.enabled = true
            csv.enabled = false
        }
        classDirectories = fileTree(
                dir : "$buildDir/intermediates/classes/debug",
                excludes : [
                        '**/*Test.class',
                        '**/R.class',
                        '**/R$*.class',
                        '**/BuildConfig.*',
                        '**/Manifest*.*'
                ]
        )
        def coverageSourceDirs = ['src/main/java']
        additionalSourceDirs = files(coverageSourceDirs)
        sourceDirectories = files(coverageSourceDirs)
        additionalClassDirs = files(coverageSourceDirs)
        executionData = files("$buildDir/outputs/code-coverage/connected/coverage.ec")
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.2.1'
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    生成报告

    配置完上面的步骤之后,打开Terminal,并输入命令gradlew jacocoTestReport,回车执行。

    命令

    之后打开下面的地址,先看下测试结果

    测试结果地址 
    测试结果

    从上图,可以看到有些测试用例是没有跑通的,点击之后可以看详情信息

    详情信息

    根据提示信息,修改代码,直到测试用例跑通之后,如下图:

    测试用例通过

    然后打开下面的地址,如果测试用例没有全部跑通的话,就不会生成代码覆盖率报告。

    代码覆盖率地址 
    代码覆盖率报告

    我们去查看下,之前跑的测试用例的覆盖率情况

    覆盖率详情

    再点击进去的话,可以看到具体有哪些分支路径是没有覆盖到的。

    将报告通过邮箱发送给相关人员

      通过上面的步骤,我们已经可以看到了结果报告,但是,我们的主题是持续集成&自动化,所以,还没有全部完成,我们的主角依然是jenkins。所以,接下来要讲的是:通过jenkins项目配置,让程序自动生成报告,并将结果通过邮件发送给相关人员。

      构建后操作 
       
      先后会创建“Publish JUnit test result report”、”Record JaCoCo coverage report “、“Publish Android Lint results”。

    配置单元测试报告

    测试报告

    这时候,报了个错误,说当前路径没有匹配到文件,没关系,因为我们还没有执行命令之前,一些文件夹还没有生成,所以可以先忽略。

    配置代码覆盖率报告

    代码覆盖率报告

    主要的几个参数配置,“Path to class directories”配置的是编译后.class文件的路径地址,Android都是放在build路径下buildintermediatesclasses;“Path to source directories”配置的是Java代码路径。

    配置Android Lint报告

    Android Lint是Android自带的一个功能,它可以检测一些不规范的写法,并提示。该功能gradle不用配置任何东西,只要执行build之后就会自动生成报告。

    Android Lint报告

    上图中不用填写入任何路径,默认的即可。

    邮件配置及gradle执行命令的修改

    首先,我们先修改下邮件的发送内容。

    邮件内容修改

    我们在上一节的基础上,只是新增加了以上3中报告的地址。 
    接着,还需要修改gradle 执行的命令。

    构建命令修改

    项目的build.gradle修改下

    build.gradle

    去掉connectedAndroidTest的关联,因为我们已经独立使用命令执行connectedAndroidTest了,所以jenkins服务器在跑job的时候,请确保已经打开了Android模拟器,否则会出错。

    查看邮件报告

    配置完以上的步骤之后,将代码push到github上,等待jenkins触发构建或者我们手动执行构建都可以。 
    邮件内容 
    邮件里增加了配置里相应修改的内容。

    注意:因为我的项目是一个lib项目,而在Android里lib项目生成的jar包是一个aar,所以这里的单元测试,我是写在lib项目里,然后构建产物,我也修改为获取aar包,修改如下: 
    构建产物修改 
    邮件附件

    总结

    跟着上面的步骤来,我们就已完成了单元测试及代码覆盖率报告的自动化发邮件了,能及时发现错误,这在很大程度上保证了我们的代码是经测试的,是有效可靠的。下一篇,将讲如何使用findbugs插件进行查虫,包括gradle的配置和jenkins的配置,发送findbugs报告到邮箱,更进一步的提高代码质量。

  • 相关阅读:
    【 DCOS 】织云 CMDB 管理引擎技术详解
    腾讯云无服务器云函数架构精解
    深度学习在 CTR 中应用
    一文教你迅速解决分布式事务 XA 一致性问题
    小程序开发工具全新上线
    秦俊:开放 DevOps 敏捷开发套件,助力开发者驰骋云端
    张兴华:云端架构助力企业快速成长
    王磊:AI 时代物流行业的 OCR 应用
    陈杰:无服务器架构,让云端开发更纯粹
    腾讯织云:DevOps 流水线应用平台践行之路
  • 原文地址:https://www.cnblogs.com/huxinping8800/p/7155638.html
Copyright © 2020-2023  润新知