Androidunit testing用匕首2

我有一个使用Dagger 2进行dependency injection的Android应用程序。 我也使用最新的gradle构build工具,它允许构build变体进行unit testing,并使用一个用于仪器testing。 我在我的应用程序中使用java.util.Random ,我想嘲笑这个testing。 我正在testing的类不使用任何Android的东西,所以他们只是普通的Java类。

在我的主代码中,我定义了一个扩展Application类的类中的Component ,但是在unit testing中我没有使用Application 。 我试过定义一个testingModuleComponent ,但Dagger不会生成Component 。 我也尝试过使用我在应用程序中定义的Component ,并在构buildModule时交换Module ,但是应用程序的Component没有针对我的testing类的inject方法。 我如何提供Random的模拟实现进行testing?

以下是一些示例代码:

应用:

 public class PipeGameApplication extends Application { private PipeGame pipeGame; @Singleton @Component(modules = PipeGameModule.class) public interface PipeGame { void inject(BoardFragment boardFragment); void inject(ConveyorFragment conveyorFragment); } @Override public void onCreate() { super.onCreate(); pipeGame = DaggerPipeGameApplication_PipeGame.create(); } public PipeGame component() { return pipeGame; } } 

模块:

 @Module public class PipeGameModule { @Provides @Singleton Random provideRandom() { return new Random(); } } 

testing的基类:

 public class BaseModelTest { PipeGameTest pipeGameTest; @Singleton @Component(modules = PipeGameTestModule.class) public interface PipeGameTest { void inject(BoardModelTest boardModelTest); void inject(ConveyorModelTest conveyorModelTest); } @Before public void setUp() { pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn't work } public PipeGameTest component() { return pipeGameTest; } } 

要么:

 public class BaseModelTest { PipeGameApplication.PipeGame pipeGameTest; // This works if I make the test module extend // the prod module, but it can't inject my test classes @Before public void setUp() { pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build(); } public PipeGameApplication.PipeGame component() { return pipeGameTest; } } 

testing模块:

 @Module public class PipeGameTestModule { @Provides @Singleton Random provideRandom() { return mock(Random.class); } } 

Dagger 2(截至2.0.0版本)目前是不可能的,没有一些解决方法。 你可以在这里阅读。

更多关于可能的解决方法:

  • 在Dagger 2.0的unit testing中如何覆盖模块/依赖项?

  • 使用Dagger2时创buildtesting依赖关系

你已经指责说:

应用程序的组件没有我的testing类的注入方法

所以,为了解决这个问题,我们可以制作一个你的Application类的testing版本。 然后我们可以有你的模块的testing版本。 为了使这一切都在testing中运行,我们可以使用Robolectric。

1)创build你的应用程序类的testing版本

 public class TestPipeGameApp extends PipeGameApp { private PipeGameModule pipeGameModule; @Override protected PipeGameModule getPipeGameModule() { if (pipeGameModule == null) { return super.pipeGameModule(); } return pipeGameModule; } public void setPipeGameModule(PipeGameModule pipeGameModule) { this.pipeGameModule = pipeGameModule; initComponent(); }} 

2)你原来的Application类需要有initComponent()pipeGameModule()方法

 public class PipeGameApp extends Application { protected void initComponent() { DaggerPipeGameComponent.builder() .pipeGameModule(getPipeGameModule()) .build(); } protected PipeGameModule pipeGameModule() { return new PipeGameModule(this); }} 

3)你的PipeGameTestModule应该用一个构造函数来扩展生产模块:

 public class PipeGameTestModule extends PipeGameModule { public PipeGameTestModule(Application app) { super(app); }} 

4)现在,在你的junittesting的setup()方法中,在你的testing应用上设置这个testing模块:

 @Before public void setup() { TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application; PipeGameTestModule module = new PipeGameTestModule(app); app.setPipeGameModule(module); } 

现在您可以自定义您的testing模块如何您最初想要的。

在我看来,你可以从不同的angular度来看待这个问题。 你将很容易能够通过不依赖于Dagger来testing你的类,这个Dagger对于正在被testing的构build类来说,其嘲讽的依赖关系被注入到它中。

我的意思是说,在testing设置中,您可以:

  • 嘲笑被testing的类的依赖关系
  • 使用嘲弄的依赖性手动构build被testing的类

由于Dagger在编译过程中validation依赖关系图的正确性,我们不需要testing依赖关系是否正确注入。 所以任何这样的错误都会由于编译失败而报告。 这就是为什么在安装方法中手动创build被testing的类应该是可以接受的。

在被testing的类中使用构造函数注入依赖关系的代码示例:

 public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(random); } @Test ... } public class BoardModel { private Random random; @Inject public BoardModel(Random random) { this.random = random; } ... } 

代码示例,其中依赖项是使用被testing的类中的字段注入的(在BoardModel由框架构造的情况下):

 public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(); boardModel.random = random; } @Test ... } public class BoardModel { @Inject Random random; public BoardModel() {} ... } 

如果您在Android上使用dagger2,则可以使用应用程序风格来提供模拟资源。

看看这里的模拟testing(没有匕首)的风味演示: https : //www.youtube.com/watch?v=vdasFFfXKOY

这个代码有一个例子: https : //github.com/googlecodelabs/android-testing

在你的/src/prod/com/yourcompany/Component.java中提供你的生产组件。

在你的/src/mock/com/yourcompany/Component.java中,你提供了你的模拟组件。

这可以让你创build你的应用程序的build设有或没有嘲笑。 它也允许并行开发(一个团队的后端,另一个团队的前端应用程序),你可以模拟,直到API方法是可用的。

我的gradle命令的外观(它是一个Makefile):

 install_mock: ./gradlew installMockDebug install: ./gradlew installProdDebug test_unit: ./gradlew testMockDebugUnitTest test_integration_mock: ./gradlew connectedMockDebugAndroidTest test_integration_prod: ./gradlew connectedProdDebugAndroidTest 

我其实有同样的问题,并find一个非常简单的解决scheme。 这不是我认为的最好的解决scheme,但它会解决你的问题。

在您的应用模块中创build一个类似的类:

 public class ActivityTest<T extends ViewModelBase> { @Inject public T vm; } 

然后,在你的AppComponent中添加:

 void inject(ActivityTest<LoginFragmentVM> activityTest); 

然后你将能够在你的testing课上注入。

  public class HelloWorldEspressoTest extends ActivityTest<LoginFragmentVM> { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class); @Test public void listGoesOverTheFold() throws InterruptedException { App.getComponent().inject(this); vm.email.set("1234"); closeSoftKeyboard(); } }