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
。 我试过定义一个testingModule
和Component
,但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(); } }