使用 robolectric 做单元测试

来源:互联网 发布:电脑能写小说的软件 编辑:程序博客网 时间:2024/05/21 18:37

简述

上文android 如何开始测试提到了测试的基本知识,我也是初次接触测试,可能存在误解,如果各位发现,还请不吝指正。

基本介绍

如果你对Robolectric不够了解,可以先阅读以下链接:

  1. Robolectric 官网指南
  2. Robolectric GitHub
  3. Robolectric 2.4 升级 3.0对比WIKI
  4. Robolectric GitBook
  5. 用Robolectric来做Android unit testing
  6. Robolectric Github 官方示例
  7. Robolectric Github 其他开发者提供的示例
  8. Robolectric Github 在开源项目Android-Boilerplate中运用

常见问题

  1. 异步任务
  2. 如何使用内置的shadow对象
  3. 准备测试环境
  4. 测试时,Application 对象获取为 null。(见下方项目实践)
  5. 测试时,需要有选择的在Application中初始化代码。见下方项目实践)
    后续遇到其他问题,再进行补充

项目实践

遇到的第一个问题:如何有选择的在 Application 初始化?

通常一段 App 的初始化代码如下。

public class App extends Application{    // ignore some code    @Override    public void onCreate () {        super.onCreate ();        if (isMainProcess ()) {            setupCrashReporting ();            setupUmeng ();            setupBasic ();            setupComponent ();        }    }    // ignore some code}

但是:

  • 如果使用setupUmeng友盟的一系列服务,可能会导致测试过程中衍伸出一些不必要的错误警告。
  • 若在App中实现了Thread.UncaughtExceptionHandler接口,来完成错误收集功能。则会导致单元测试时,无法正常执行。
  • 亦或者,需要在单元测试时初始化一些正常运行时额外的一些服务。

但是通常,直接更改App会导致代码中的职责界限不清晰。

我选择的做法是在 src/test/java目录下扩展App子类UnitTestApp如下。

public class UnitTestApp extends App {    @Override    public void setupCrashReporting () {    }    @Override    public void setupUmeng () {    }    @Override    protected void setupComponent () {        CollectInfoUtil.init (this);        initVersionInfo ();    }    @Override    public boolean isMainProcess () {        return true;    }}

这生产了一个仅测试使用的UnitTestApp,但是如何测试时使用的是UnitTestApp,而不是App。你需要自定义TestRunner,如下:

public class UnitTestGradleTestRunner extends RobolectricGradleTestRunner {    // This value should be changed as soon as Robolectric will support newer api.    private static final int SDK_EMULATE_LEVEL = 21;    public UnitTestGradleTestRunner (Class<?> klass) throws InitializationError {        super (klass);    }    @Override    public Config getConfig (Method method) {        final Config defaultConfig = super.getConfig (method);        return new Config.Implementation (                new int[]{SDK_EMULATE_LEVEL},                defaultConfig.manifest (),                defaultConfig.qualifiers (),                defaultConfig.packageName (),                defaultConfig.resourceDir (),                defaultConfig.assetDir (),                defaultConfig.shadows (),                UnitTestApp.class, // Here is the trick, we change application class to one with mocks.                defaultConfig.libraries (),                defaultConfig.constants () == Void.class ? BuildConfig.class : defaultConfig.constants ()        );    }    @NonNull    public static UnitTestApp getApp () {        return (UnitTestApp) RuntimeEnvironment.application;    }}

在你需要使用到App的测试类中指定如下:

@RunWith (UnitTestGradleTestRunner.class)@Config (constants = BuildConfig.class)public class LoginActivityTest{@Before    public void setUp () throws Exception {        UnitTestApp app = UnitTestGradleTestRunner.getApp ();        Assert.assertNotNull ("shadowApp not null.", app);        Assert.assertNotNull ("shadowApp context not null.", app.getApplicationContext ());    }}

其他建议

在测试过程中,经常会需要对测试代码进行 set/get操作。但是大部分时候,一些方法或者域是private或者protect,为了保证测试功能代码的唯一性,我选择使用reflect方式,来确保测试工作的顺利。我使用的reflection-utils来完成反射工作。

结束语

robolectric的基本认识如上,但是具体在测试中很多问题需要逐一解决,后续再一一整理出来。

希望大家多拍砖,特别是有错误的地方重拍都没事~

0 0
原创粉丝点击