[英]How can I replace Activity scoped dependencies with mocks using Dagger2
我的Activity中有一个作用域依赖项,我想用一些模拟测试该活动。 我已经阅读了有关在测试期间建议用测试组件替换Application组件的不同方法,但我想要的是替换Activity组件。
例如,我想在我的MVP设置中测试针对模拟演示者的Activity。
我相信通过调用Activity上的setComponent()替换组件将不起作用,因为Activity依赖已经通过字段注入注入,因此在测试期间,将使用真实对象。
我该如何解决这个问题? Dagger1怎么样? 它有同样的问题吗?
注入组件
首先,创建一个静态类作为Activity的工厂。 我看起来有点像这样:
public class ActivityComponentFactory {
private static ActivityComponentFactory sInstance;
public static ActivityComponentFactory getInstance() {
if(sInstance == null) sInstance = new ActivityComponentFactory();
return sInstance;
}
@VisibleForTesting
public static void setInstance(ActivityComponentFactory instance) {
sInstance = instance;
}
private ActivityComponentFactory() {
// Singleton
}
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.create();
}
}
然后只做ActivityComponentFactory.getInstance().createActivityComponent().inject(this);
在你的活动中。
对于测试,您可以在创建活动之前替换方法中的工厂。
提供嘲笑
正如@ EpicPandaForce的答案所表明的那样,这样做的官方支持方式目前涉及大量的样板和复制/粘贴代码。 Dagger 2团队需要提供一种更简单的部分覆盖模块的方法。
直到他们这样做,这是我的非正式方式: 只需扩展模块 。
假设您想用模拟替换ListViewPresenter。 假设您有一个PresenterModule,如下所示:
@Module @ActivityScope
public class PresenterModule {
@ActivityScope
public ListViewPresenter provideListViewPresenter() {
return new ListViewPresenter();
}
@ActivityScope
public SomeOtherPresenter provideSomeOtherPresenter() {
return new SomeOtherPresenter();
}
}
您可以在测试设置中执行此操作:
ActivityComponentFactory.setInstance(new ActivityComponentFactory() {
@Override
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.builder()
.presenterModule(new PresenterModule() {
@Override
public ListViewPresenter provideListViewPresenter() {
// Note you don't have to use Mockito, it's just what I use
return Mockito.mock(ListViewPresenter.class);
}
})
.build();
}
});
......它只是有效 !
请注意,您不必包括@Provides
上注释@Override
方法。 事实上,如果你这样做,Dagger 2代码生成将失败。
这是有效的,因为模块只是简单的工厂 - 生成的Component类负责缓存作用域实例的实例。 @Scope
注释由代码生成器使用,但在运行时无关紧要。
你不能在Dagger2中覆盖模块[ 编辑:你可以,只是不要在模拟上指定@Provides
注释 ),这显然是正确的解决方案:只需使用builder().somethingModule(new MockSomethingModule()).build()
并完成它!
如果您认为无法进行模拟,那么我会看到两个可能解决此问题的方法。 你可以使用模块来包含一个可插入的“提供者”,它可以改变它的实现(我不赞成这个,因为它太冗长了!)
public interface SomethingProvider {
Something something(Context context);
}
@Module
public class SomethingModule {
private SomethingProvider somethingProvider;
public SomethingModule(SomethingProvider somethingProvider) {
this.somethingProvider = somethingProvider;
}
@Provides
@Singleton
public Something something(Context context) {
return somethingProvider.something(context);
}
}
public class ProdSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new SomethingImpl(context);
}
}
public class TestSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new MockSomethingImpl(context);
}
}
SomethingComponent somethingComponent = DaggerSomethingComponent.builder()
.somethingModule(new SomethingModule(new ProdSomethingProvider()))
.build();
或者,您可以将提供的类和注入目标放入他们自己的“元组件”接口中,您的ApplicationComponent
和TestApplicationComponent
将从该接口扩展。
public interface MetaApplicationComponent {
Something something();
void inject(MainActivity mainActivity);
}
@Component(modules={SomethingModule.class})
@Singleton
public interface ApplicationComponent extends MetaApplicationComponent {
}
@Component(modules={MockSomethingModule.class})
@Singleton
public interface MockApplicationComponent extends MetaApplicationComponent {
}
第三个解决方案就是像@vaughandroid的回答一样扩展模块。 请参考,这是正确的方法。
至于活动范围的组件......就像我在这里提到的那样,它只是一个不同的范围,真的。
我发现以下帖子解决了这个问题: http : //blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html
您首先需要允许修改活动的组件:
@Override public void onCreate() {
super.onCreate();
if (component == null) {
component = DaggerDemoApplication_ApplicationComponent
.builder()
.clockModule(new ClockModule())
.build();
}
}
public void setComponent(DemoComponent component) {
this.component = component;
}
public DemoComponent component() {
return component;
}
并在测试用例中修改它
@Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
DemoApplication app
= (DemoApplication) instrumentation.getTargetContext().getApplicationContext();
TestComponent component = DaggerMainActivityTest_TestComponent.builder()
.mockClockModule(new MockClockModule())
.build();
app.setComponent(component);
component.inject(this);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.