我们的测试用聚焦在应用程序的业务逻辑上,凭经验来说一个好的单元测试应该有如下测试分布:
70-80 % 是单元测试,保证基础代码功能的稳定
20-30 % 功能性测试,保证我们的应用程序可以运行
如果我们继承了其他应用组件的话,还需一部分交叉功能测试。
Android测试分为本地测试(Local Unit Tests)和仪器测试(Instrumented Tests):
运行速度快;
无需连接Android真机或者模拟器;
对应目录是src/test/目录;
生成的报告文件是*.exec;
可视化查看报告目录:可在Android studio自带的coverage侧边栏插件更直观的查看。
运行速度慢;
需要连接Android真机或者模拟器;
对应目录是src/androidTest/目录;
生成的报告文件是*.ec;
可视化查看报告目录:YOUR_PROJECT_PATH\app\build\reports\coverage\debug\index.html
测试报告生成
由于仪器测试运行速度慢,需要连接设备等不便因素影响,暂时只提供本地测试对接sonar配置。
希望Sonar上体现测试覆盖率时,则需将生成的报告文件提交到sonar。
这里用的jacoco插件用的是vanniktech提供的jacoco,另外gradle默认支持的是EclEmma提供的jacoco插件。
之所以不选用gradle默认的jacoco插件,是因为其不支持productFlavors配置,而vanniktech修复了该Bug。
vanniktech支持productFlavors修复反馈
Gradle Jacoco Plugin By Vanniktech
Gradle Jacoco Plugin By Default
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.com.vanniktech:gradle-android-junit-jacoco-plugin:0.13.0"
}
}
apply plugin: "com.vanniktech.android.junit.jacoco"
apply from: 'sonarqube.gradle'//create new sonar config.
这里创建sonarqube.gradle,一方面统一并完善sonar配置,一方面添加测试报告关联配置。
创建sonarqube.gradle于工程根目录下,
apply plugin: "org.sonarqube"
ext {
SONAR_HOST = "http://...sonar-server-url...:9000/"
}
sonarqube {
properties {
property "sonar.host.url", SONAR_HOST
//projectName根据QA要求命名
property "sonar.projectName", "longfor-xxx"
//projectKey建议使用项目的applicationId,注意相同的projectName,但不同的projectKey将在sonarqube上生成两个不同的项目!
property "sonar.projectKey", rootProject.ext.applicationId
property "sonar.sourceEncoding", "UTF-8"
//Excluding src/androidTest/** and src/test/** is not allowed!
//exclusions可以配置多个,以逗号分割
property "sonar.exclusions", "src/main/res/**"
property "sonar.sources", "src/main/"
property "sonar.tests", "src/test/"
//本地测试(Local Unit Tests)报告上传配置,注意sonar.jacoco.reportPath已弃用,使用sonar.jacoco.reportPaths.
property "sonar.jacoco.reportPaths", fileTree(dir: project.projectDir, includes: ['**/*.exec'])
//Instrumented Tests报告上传,目前未做上传
// property "sonar.jacoco.itReportPath", fileTree(dir: project.projectDir, includes: ['**/*.ec'])
}
}
//针对项目中其他Library Module进行单独配置
project(":base") {
sonarqube {
//不扫描该Module
skipProject = true
}
}
project(":biz") {
sonarqube {
properties {
//不扫描该Module下的指定目录
property "sonar.exclusions", "build/**," +
"src/main/res/**," +
"src/main/java/db/**"
}
}
}
Application Module即为build.gradle中声明
apply plugin: 'com.android.application'
的Module。
在其build.gradle中 添加 本地单元测试插件:
dependencies {
...此处省略其他包依赖
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
...此处省略其他包依赖
}
//将buildType为debug环境下的扫描报告上传sonar。注意指令顺序!!!
gradle jacocoTestReportDebug sonarqube --stacktrace
gradle 语句: 该命令因电脑系统mac/window,本地有无特殊配置而不同,其他写法有:./gradle ./gradlew等
jacocoTestReportDebug 语句: 该语句语法为jacocoTestReport,
即,在没有配置productFlavors的项目中只有jacocoTestReportDebug和jacocoTestReportRelease,
如果在productFlavors中配置了dev和uat,那么将会有:jacocoTestReportdevDebug,jacocoTestReportuatDebug,jacocoTestReportdevRelease,jacocoTestReportuatRelease.
sonarqube 语句: 上传到sonarqube.
–stacktrace 语句: 用于打印出生成测试报告,上传soanrqube过程中的报错信息,可写可不写。
本地单元测试代码示例
src/main/源代码:
public class StringUtils {
private StringUtils() {
}
public static boolean isNull(String s) {
return null == s || s.trim().length() <= 0;
}
public static boolean canParseInt(String s) {
try {
Integer.parseInt(s);
return true;
} catch (Exception e) {
}
return false;
}
}
src/test/源代码:
public class StringUtilsTest {
private String canParseInt_Value;
@Before
public void setUp() throws Exception {
canParseInt_Value = "6";
}
@After
public void tearDown() throws Exception {
}
@Test
public void isNull() {
assertThat(StringUtils.isNull(""), is(true));
assertThat(StringUtils.isNull(null), is(true));
assertThat(StringUtils.isNull("ab"), is(false));
}
@Test
public void canParseInt() {
assertThat(StringUtils.canParseInt(canParseInt_Value), is(true));
assertThat(StringUtils.canParseInt("a"), is(false));
}
}
1、sonar配置中,相同的projectName,但不同的projectKey将在sonarqube上生成两个不同的项目!