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 则需要稍稍配置一下,这里提供两种方式进行配置。

2.1 给每个测试类单独配置

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

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

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

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。

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

3.3 如何测试Dialog的显示

某些情况下,弹出Dialog。

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

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

3.4 如何测试Toast的显示

某些情况下,弹出 Toast

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

可以测试 Toast 的显示,以及 Toast 的内容

3.5 如何设置测试用例的Application

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

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

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

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

转载于:https://juejin.im/post/5c97528c5188252d64583116

你可能感兴趣的:(Android单元测试编写二三事)