由于客户要求手机端APP要有单元测试并达到一定的覆盖率,本人花了几天研究一下AndroidStudio下单元测试的环境配置,经历了大大小小不少坑,这里写出来给需要的小伙伴一个参考。
1.概念
安卓测试分为AndroidInstrumentTest和AndroidUnitTest。区别是前者需要安卓模拟器或实机测试,后者只需JVM环境。
使用AndroidStudio创建Android项目后可以看到src下有如下目录
其中androidTest存在AndroidInstrumentTest代码,test存在AndroidUnitTest代码。
相比较来说,使用AndroidUnitTest更舒服一些。AndroidInstrumentTest的话大家可以想象一下在模拟器或实机上调试Android工程,速度是比较慢的。
所以这里配置的是AndroidUnitTest的环境。
2.配置
AndroidStudio自带JUnit,但是这个尚不足够。Android程序是运行在DVM上的,想要他在JVM环境下直接运行,就需要一个模拟环境。这就需要第三方测试框架Robolectric。具体就不介绍了,大家可以上官方网站自己看,配置方法也很简单:
修改build.gradle里的dependencies
dependencies {
...
testCompile ('junit:junit:4.12')
testCompile ('org.robolectric:robolectric:3.2.2')
testCompile ("org.robolectric:shadows-support-v4:3.2")
...
}
其中shadows-support-v4提供了对应support-v4包的测试类,如果不需要删除。
测试类配置如下
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 22)
public class ExampleUnitTest {
@Before
public void setup()
{
...
}
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
是不是很简单?然而如果仅仅是这样就没必要写这篇文章了,请大家往下看
3.遇到的坑
如果你在国内且没有VPN,在配置dependencies后,你就会发现gradle在自动build下载时很慢。就算你耐心等待它完成后,在运行时你会发现Robolectric会需要根据你的配置进一步下载一些包,包很大,下载速度却慢的令人发指。
这里推荐使用aliyun的国内maven镜像库。
修改工程目录下的build.gradle
buildscript {
repositories {
jcenter(){ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
allprojects {
repositories {
jcenter(){ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Gradle的build问题就解决了。但是Robolectric呢?
官方网站上推荐设置systemProperty来手动修改库的url,修改方法如下
android {
testOptions {
unitTests.all {
systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
systemProperty 'robolectric.dependency.repo.id', 'local'
}
}
}
然而这不起作用,所以这里需要修改RobolectricTestRunner.class。
本人这里就直接在网上找个代码。当然到GitHub上找到Robolectric原代码自己重写也是可以的
public class CustomRepoRobolectricTestRunner extends RobolectricTestRunner {
public static final String REP_URL = "http://maven.aliyun.com/nexus/content/groups/public/";
public static final String REP_ID = "aliyun_mirror";
public CustomRepoRobolectricTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
//覆盖该方法 可以定制DependencyResolver
@Override
protected DependencyResolver getJarResolver() {
// String repoUrl = System.getProperty("robolectric.dependency.repo.url", ".");
// String repoId = System.getProperty("robolectric.dependency.repo.id", ".");
String repoUrl = REP_URL;
String repoId = REP_ID;
if (StringUtils.isNotEmpty(repoUrl) && StringUtils.isNotEmpty(repoId)) {
File cacheDir = new File(new File(System.getProperty("java.io.tmpdir")), "robolectric");
cacheDir.mkdir();
DependencyResolver dependencyResolver;
//如果设置了robolectric.dependency.repo.url属性,则利用LocalNetDependencyResolver来解决依赖
//这样便可以调整仓库地址
if (cacheDir.exists()) {
Logger.info("Dependency cache location: %s", cacheDir.getAbsolutePath());
dependencyResolver = new CachedDependencyResolver(
new LocalNetDependencyResolver(repoUrl, repoId), cacheDir, 60 * 60 * 24 * 1000
);
} else {
dependencyResolver = new LocalNetDependencyResolver(repoUrl, repoId);
}
return dependencyResolver;
} else {
return super.getJarResolver();
}
}
private static class LocalNetDependencyResolver implements DependencyResolver {
private String mRepoUrl;
private String mRepoId;
private final Project project = new Project();
public LocalNetDependencyResolver(String repoUrl, String repoId) {
mRepoId = repoId;
mRepoUrl = repoUrl;
}
@Override
public URL[] getLocalArtifactUrls(DependencyJar... dependencies) {
DependenciesTask dependenciesTask = new DependenciesTask();
configureMaven(dependenciesTask);
RemoteRepository sonatypeRepository = new RemoteRepository();
sonatypeRepository.setUrl(mRepoUrl);
sonatypeRepository.setId(mRepoId);
dependenciesTask.addConfiguredRemoteRepository(sonatypeRepository);
dependenciesTask.setProject(project);
for (DependencyJar dependencyJar : dependencies) {
Dependency dependency = new Dependency();
dependency.setArtifactId(dependencyJar.getArtifactId());
dependency.setGroupId(dependencyJar.getGroupId());
dependency.setType(dependencyJar.getType());
dependency.setVersion(dependencyJar.getVersion());
if (dependencyJar.getClassifier() != null) {
dependency.setClassifier(dependencyJar.getClassifier());
}
dependenciesTask.addDependency(dependency);
}
dependenciesTask.execute();
@SuppressWarnings("unchecked")
Hashtable<String, String> artifacts = project.getProperties();
URL[] urls = new URL[dependencies.length];
for (int i = 0; i < urls.length; i++) {
try {
urls[i] = Util.url(artifacts.get(key(dependencies[i])));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return urls;
}
@Override
public URL getLocalArtifactUrl(DependencyJar dependency) {
URL[] urls = getLocalArtifactUrls(dependency);
if (urls.length > 0) {
return urls[0];
}
return null;
}
private String key(DependencyJar dependency) {
String key = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":"
+ dependency.getType();
if (dependency.getClassifier() != null) {
key += ":" + dependency.getClassifier();
}
return key;
}
protected void configureMaven(DependenciesTask dependenciesTask) {
// maybe you want to override this method and some settings?
}
}
}
这里我直接在代码中写了URL,其实通过Gradle配置SystemProperty更为合理。
修改测试代码
@RunWith(CustomRepoRobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 22)
public class ExampleUnitTest {
@Before
public void setup()
{
}
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
这样就配置后可以看到能跑起来了。
还有个注意点Robolectric的sdk版本推荐配置为22,不要试图在更高版本上运行。
参考:
1.http://blog.csdn.net/x356982611/article/details/21983267
2.http://robolectric.org/
3.https://github.com/robolectric/robolectric
本文介绍如何配置Android Studio下的单元测试环境,并重点讲解如何使用Robolectric进行AndroidUnitTest配置,包括解决国内下载速度慢的问题。

8369

被折叠的 条评论
为什么被折叠?



