@tomrozb 提出的解决方法非常好,让我走上了正轨,但我的问题是它setTestComponent()
在 PRODUCTIONApplication
类中暴露了一个方法。我能够让这个工作略有不同,这样我的生产应用程序就不必知道任何关于我的测试环境的事情。
TL;DR - 通过使用您的测试组件和模块的测试应用程序扩展您的应用程序类。然后创建一个在测试应用程序而不是生产应用程序上运行的自定义测试运行程序。
编辑:此方法仅适用于全局依赖项(通常标有@Singleton
)。如果您的应用程序具有不同范围的组件(例如每个活动),那么您需要为每个范围创建子类,或者使用@tomrozb 的原始答案。感谢@tomrozb 指出这一点!
此示例使用AndroidJUnitRunner测试运行程序,但这可能适用于Robolectric等。
首先,我的生产应用程序。它看起来像这样:
public class MyApp extends Application {
protected MyComponent component;
public void setComponent() {
component = DaggerMyComponent.builder()
.myModule(new MyModule())
.build();
component.inject(this);
}
public MyComponent getComponent() {
return component;
}
@Override
public void onCreate() {
super.onCreate();
setComponent();
}
}
这样,我的活动和其他使用的类@Inject
只需调用类似getApp().getComponent().inject(this);
将自己注入依赖关系图中的东西。
为了完整起见,这是我的组件:
@Singleton
@Component(modules = {MyModule.class})
public interface MyComponent {
void inject(MyApp app);
// other injects and getters
}
我的模块:
@Module
public class MyModule {
// EDIT: This solution only works for global dependencies
@Provides @Singleton
public MyClass provideMyClass() { ... }
// ... other providers
}
对于测试环境,从生产组件扩展您的测试组件。这与@tomrozb 的答案相同。
@Singleton
@Component(modules = {MyTestModule.class})
public interface MyTestComponent extends MyComponent {
// more component methods if necessary
}
测试模块可以是任何你想要的。大概你会在这里处理你的嘲笑和东西(我使用 Mockito)。
@Module
public class MyTestModule {
// EDIT: This solution only works for global dependencies
@Provides @Singleton
public MyClass provideMyClass() { ... }
// Make sure to implement all the same methods here that are in MyModule,
// even though it's not an override.
}
所以现在,棘手的部分。创建一个从您的生产应用程序类扩展的测试应用程序类,并覆盖该setComponent()
方法以使用测试模块设置测试组件。请注意,这只有在MyTestComponent
是 的后代时才有效MyComponent
。
public class MyTestApp extends MyApp {
// Make sure to call this method during setup of your tests!
@Override
public void setComponent() {
component = DaggerMyTestComponent.builder()
.myTestModule(new MyTestModule())
.build();
component.inject(this)
}
}
确保在开始测试之前调用setComponent()
应用程序,以确保图表设置正确。像这样的东西:
@Before
public void setUp() {
MyTestApp app = (MyTestApp) getInstrumentation().getTargetContext().getApplicationContext();
app.setComponent()
((MyTestComponent) app.getComponent()).inject(this)
}
最后,最后缺少的部分是使用自定义测试运行器覆盖您的 TestRunner。在我的项目中,我使用的是,AndroidJUnitRunner
但看起来你可以用 Robolectric 做同样的事情。
public class TestRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(@NonNull ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, MyTestApp.class.getName(), context);
}
}
您还必须更新您的testInstrumentationRunner
gradle,如下所示:
testInstrumentationRunner "com.mypackage.TestRunner"
如果您使用的是 Android Studio,您还必须从运行菜单中单击编辑配置,然后在“特定检测运行程序”下输入您的测试运行程序的名称。
就是这样!希望这些信息对某人有所帮助:)