原文:Mock Application in Espresso for Dependency Injection
作者:Chiu-Ki Chan
译者:lovexiaov
我看了 Artem Zinnatullin 写的使用 Dagger,Robolectric 和 InStrumentation 在单元测试,集成测试与功能测试中模拟依赖(译者注:这里将链接替换成了中译版本,文章中有原文链接)这篇好文。其中我最喜欢的部分是在测试中使用不同的 application
来提供不同的依赖,而我决定使用 Espresso 实现它。
通过自定义测试运行器模拟 application
我当前实现依赖注入(译者注:下文统一使用 DI)的方法是在我的测试 application 中暴露一个 setComponent
函数来提供测试组件,这样并不是太好,因为理想情况下 application 中不能包含测试指定代码。
一种新的实现方式是在 androidTest
文件夹中使用 application 的子类 ,并在测试时通过自定义测试运行器加载它。
public class DemoApplication extends Application {
private final DemoComponent component = createComponent();
protected DemoComponent createComponent() {
return DaggerDemoApplication_ApplicationComponent.builder()
.clockModule(new ClockModule())
.build();
}
public DemoComponent component() {
return component;
}
}
在此 application 中,我们使用 createComponent()
实例化的 DemoComponent
,并且将它存储为 final
变量待以后使用。
public class MockDemoApplication extends DemoApplication {
@Override
protected DemoComponent createComponent() {
return DaggerMainActivityTest_TestComponent.builder()
.mockClockModule(new MockClockModule())
.build();
}
}
测试时,我们继承自己的 application 并重写 createComponent
来提供测试组件。
我们需要自定义测试运行器以在测试是使用模拟 application:
public class MockTestRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(
ClassLoader cl, String className, Context context)
throws InstantiationException,
IllegalAccessException,
ClassNotFoundException {
return super.newApplication(
cl, MockDemoApplication.class.getName(), context);
}
}
我们给出 MockDemoApplication.class.getName()
作为类名,这样测试运行器将会加载模拟 application 而不是真实的 application。
按应用还是按测试?
此方式与 setComponent
有些许不同,因为我们只初始化测试组件一次,而不是每个测试方法执行前都要初始化。确保你在每次测试方法执行前都清除了测试模块的状态,这样每个测试方法都能从零开始执行。
源码
我已经在我的两个仓库中使用了此方式:
- android-test-demo:迷你样例演示的此概念。
- friendspell:一个真实的应用展示了怎样在每个测试之前清除状态。