Android单元测试环境配置指南

来源:互联网 发布:目前淘宝卖什么最火 编辑:程序博客网 时间:2024/06/05 06:30

由于客户要求手机端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

0 0
原创粉丝点击