Android单元测试编写二三事

0. 环境

  • Mac OS 10.13.4
  • Android Studio 3.1.2
  • Junit 4.12
  • Robolectric 3.7.1
  • Mockito 2.13.0
  • PowerMock 2.0.0-beta.5

1. 预备知识

1.1 JUnit4

  1. 什么是 JUnit4

1.2 Robolectric

  1. 什么是 Robolectric

1.3 Mockito

  1. 什么是 Mockito

1.4 PowerMock

  1. 什么是 PowerMock
  2. PowerMock 简介

2. 配置 Robolectric

单元测试主要基于 JUnit 和 Robolectric 进行。Android Studio 默认集成好了 JUnit,而 Robolectric 则须要稍稍配置一下,这里提供两种方式进行配置。html

2.1 给每一个测试类单独配置

在每一个测试类上都加上注解java

@RunWith(RobolectricTestRunner.class)
@Config(application = TestMyApplication.class, constants = BuildConfig.class, manifest = "./AndroidManifest.xml",packageName = "com.your.package")
复制代码

其中,TestMyApplication是测试代码的初始化Application,能够把App的Application的逻辑放在这里面android

2.2 在 TestMyRobolectricRunner 中集中配置

  1. 自定义 TestMyRobolectricRunner,继承自 RobolectricTestRunner
  2. 自定义 TestMyApplication,这个 TestMyApplication 承担的做用是初始化一些东西,好比 SDK 等,和 App 中的 Application 做用是同样的
  3. 在 TestMyRobolectricRunner 中重写 buildGlobalConfig 方法
  4. 在测试类中,就能够再也不使用 @RunWith(RobolectricTestRunner.class) 注解,转而使用 @RunWith(TestMyRobolectricRunner.class) 注解
public class TestMyRobolectricRunner extends RobolectricTestRunner {

    /** * Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file * and res directory by default. Use the {@link Config} annotation to configure. * * @param testClass the test class to be run * @throws InitializationError if junit says so */
    public TestMyRobolectricRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    @Override
    protected Config buildGlobalConfig() {
        return new Config.Builder()
                .setApplication(TestMyApplication.class)
                .setConstants(BuildConfig.class)
                .setManifest("AndroidManifest.xml")
                .setPackageName("com.yongf.mypackagename")
                .build();
    }
}
复制代码

3. Android 单测中的一些 case

3.1 如何测试 SharedPreference

SharedPreferences preferences = RuntimeEnvironment.application.getSharedPreferences(PREFERENCE_KEY, Context.MODE_PRIVATE);
复制代码

3.2 如何测试Intent跳转

好比,点击一个按钮之后在知足条件的状况下,会跳转到一个新页面。怎么测呢,能够先知足各类设置条件,而后模拟按钮点击,而后获取系统跳转的下一个 activity 是否指定的 activity。git

//按钮点击后跳转到下一个Activity
forwardBtn.performClick();
Intent expectedIntent = new Intent(sampleActivity, LoginActivity.class);
Intent actualIntent = ShadowApplication.getInstance().getNextStartedActivity();
assertEquals(expectedIntent, actualIntent);
复制代码

3.3 如何测试Dialog的显示

某些状况下,弹出Dialog。github

ShadowDialog latestDialog = ShadowApplication.getInstance().getLatestDialog();
复制代码

遗憾的是,目前只能测试是否有对话框弹出,并不能测试对话框的内容(PS: 若是观众朋友你会的话,千万别吝啬你的才华,请在评论中或者 issue 中赐教~)app

3.4 如何测试Toast的显示

某些状况下,弹出 Toastide

Toast latestToast = ShadowToast.getLatestToast();
String textOfLatestToast = ShadowToast.getTextOfLatestToast();
复制代码

能够测试 Toast 的显示,以及 Toast 的内容单元测试

3.5 如何设置测试用例的Application

参考上述 配置 Robolectric 章节的示例代码测试

3.6 如何指定单个测试[类|方法]的sdk版本

单个测试类,在[类|方法]上面添加 @Config(sdk = 21) 便可,具体的 sdk 版本本身指定ui

3.7 须要用户权限受权的场景如何测试

目前个人作法是指定 sdk 版本为21如下,由于没有动态权限,这样须要用户受权的部分就能走。但这样带来的问题就是,权限未受权的分支部分代码没有被覆盖到。

若是你有更好的作法,赶忙来告诉我吧,谢谢啦~

3.8 如何测试 onActivityResult

  1. 使用 Robolectric 构造一个 activity
  2. 而后直接调用 onActivityResult 方法

3.9 若是出现 not mocked,怎么办?

在运行的测试用例的时候,你可能会遇到以下错误:

java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.text.TextUtils.isEmpty(TextUtils.java)
at com.example.robolectric.TextUtilsTest.testIsEmpty(TextUtilsTest.java:14)
复制代码

如何解决呢?很简单,把 Android 源码中这个类搬到测试代码目录下便可(注意要保持包名一致)

4. 单元测试编写实战

TODO

4.1 搭建环境

  1. 新建 demo 项目
  2. 引入相关依赖
  3. TODO
相关文章
相关标签/搜索