DaggerMock 使用文件
DaggerMock 是一個 JUnit rule, 方便覆蓋 Dagger2 的 dependency
更多關於 Dagger2 和 Mockito 測試的文章可以檢視 Medium Post
覆蓋一個 Dagger2 建立的 dependency 是很麻煩的, 你需要定義一個 TestModule. 如果你想注入一個用於測試的 dependency, 還需要定義一個 TestComponent.
使用 DaggerMockRule
, 你能夠更方便地覆蓋一個由 Dagger2 的 module 生成的 dependency:
public class MainServiceTest {
@Rule public DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
.set(new DaggerMockRule.ComponentSetter<MyComponent>() {
@Override public void setComponent(MyComponent component) {
mainService = component.mainService();
}
});
@Mock RestService restService;
@Mock MyPrinter myPrinter;
MainService mainService;
@Test
public void testDoSomething() {
when(restService.getSomething()).thenReturn("abc");
mainService.doSomething();
verify(myPrinter).print("ABC");
}
}
當 DaggerMockRule
rule 例項化的時候, 它會查詢測試類中的 @Mock 註解的欄位, 如果這個欄位在 module 中有提供, 則為這個提供的物件建立一個 mock 物件並注入到這個欄位.
在 MyModule 中提供了 RestService
和 MyPrinter
兩個 dependency. 在幕後, DaggerMockRule
rule 會建立一個新的 MyModule
覆蓋原來的 module, 然後返回 MyPrinter
和 RestService
的 mock 物件, 如下:
public class TestModule extends MyModule {
@Override public MyPrinter provideMyPrinter() {
return Mockito.mock(MyPrinter.class);
}
@Override public RestService provideRestService() {
return Mockito.mock(RestService.class);
}
}
DaggerMock 只能覆蓋 Dagger 中使用 module 提供的 dependency, 不能覆蓋使用 Inject
定義的物件. 0.6 版本之後, 如果使用了 Inject
定義的物件就報錯 runtime error
.
支援 Espresso
DaggerMockRule
可以用在 Espresso 中:
public class MainActivityTest {
@Rule public DaggerMockRule<MyComponent> daggerRule = new DaggerMockRule<>(MyComponent.class, new MyModule())
.set(new DaggerMockRule.ComponentSetter<MyComponent>() {
@Override public void setComponent(MyComponent component) {
App app = (App) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
app.setComponent(component);
}
});
@Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Mock RestService restService;
@Mock MyPrinter myPrinter;
@Test
public void testCreateActivity() {
when(restService.getSomething()).thenReturn("abc");
activityRule.launchActivity(null);
verify(myPrinter).print("ABC");
}
}
支援 Robolectric
類似的, 可以在 Robolectric 中使用:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
@Rule public final DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
.set(new DaggerMockRule.ComponentSetter<MyComponent>() {
@Override public void setComponent(MyComponent component) {
((App) RuntimeEnvironment.application).setComponent(component);
}
});
@Mock RestService restService;
@Mock MyPrinter myPrinter;
@Test
public void testCreateActivity() {
when(restService.getSomething()).thenReturn("abc");
Robolectric.setupActivity(MainActivity.class);
verify(myPrinter).print("ABC");
}
}
InjectFromComponent 註解
在第一個樣例中, 我們使用 ComponentSetter 把 component 中的 dependency 注入到欄位中:
@Rule public DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
.set(new DaggerMockRule.ComponentSetter<MyComponent>() {
@Override public void setComponent(MyComponent component) {
mainService = component.mainService(); // 注入到 mainService
}
});
MainService mainService;
0.6 版本之後, 我們可以使用 InjectFromComponent
獲取 dependency
public class MainServiceTest {
@Rule public final DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule());
@Mock RestService restService;
@Mock MyPrinter myPrinter;
@InjectFromComponent MainService mainService;
@Test
public void testDoSomething() {
when(restService.getSomething()).thenReturn("abc");
mainService.doSomething();
verify(myPrinter).print("ABC");
}
}
注: @Mock
是 DaggerMock 建立的 Module 提供的 mock 物件, @InjectFromComponent
是原 component 或者原 module 提供的物件, 一般用於注入被測試的物件.
很多 Dagger 提供的 dependency 是不能通過 component 獲取到的, 這時候可以使用下面的方式獲取:
@InjectFromComponent(MainActivity.class) MainService mainService;
@InjectFromComponent({MainActivity.class, MainPresenter.class}) MainService mainService;
注: 檢視 DaggerMockRule 可知, InjectFromComponent 實現原理是: 1. 沒有引數時, 從 component 或者 module 中獲取; 2. 有引數時, 如上面例子2, 用反射建立一個物件 MainActivity, 獲取 MainActivity 中的欄位 MainPresenter, 獲取 MainPresenter 中的欄位 MainService, 賦值給 mainService
自定義規則
可以建立一個 MyRule
繼承自 DaggerMockRule
複用程式碼
public class MyRule extends DaggerMockRule<MyComponent> {
public MyRule() {
super(MyComponent.class, new MyModule());
set(new DaggerMockRule.ComponentSetter<MyComponent>() {
@Override public void setComponent(MyComponent component) {
App app = (App) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
app.setComponent(component);
}
});
}
}
在 Espresso 中使用如下:
public class MainActivityTest {
@Rule public MyRule daggerRule = new MyRule();
@Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Mock RestService restService;
@Mock MyPrinter myPrinter;
@Test
public void testCreateActivity() {
when(restService.getSomething()).thenReturn("abc");
activityRule.launchActivity(null);
verify(myPrinter).print("ABC");
}
}
Dagger Subcomponents
0.6 版本之後, DaggerMock 開始支援 Subcomponents, 但是有一些限制: subcomponent module 必須作為 subcomponent 在其父 Component 宣告時的引數. 例如: 定義一個 subcomponent:
@Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
subcomponent 在其父 Component 中宣告:
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
// 返回 Subcomponent, 方法的引數為 modules
MainActivityComponent activityComponent(MainActivityModule module);
}
Subcomponent 需要在 Dagger 2.1+ 版本才支援, 例子可以參考這裡
DaggerMock 配置
在專案 build.gradle 中配置 (專案根目錄):
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
在 module 中新增依賴:
dependencies {
testCompile 'com.github.fabioCollini:DaggerMock:0.6.2'
//and/or 如果你需要在Instrumentation、Espresso、UiAutomator裡面使用的話
androidTestCompile 'com.github.fabioCollini:DaggerMock:0.6.2'
}
相關文章
- 如何使用go文件Go
- ibatis 使用文件BAT
- 使用apidoc文件神器,快速生成api文件API
- flask-maple使用文件Flask
- Express 文件(使用模板引擎)Express
- API文件使用方法API
- 使用VuePress開心地寫文件Vue
- Express 文件(使用中介軟體)Express
- fastjson使用說明文件ASTJSON
- Master-Worker工具使用文件AST
- openvpn安裝使用文件
- 在RPA專案中有哪些文件,如何使用這些文件
- ApiBoot - ApiBoot Quartz 使用文件APIbootquartz
- jQuery如何使用文件操作detach()方法jQuery
- 文件模型(JSON)使用介紹模型JSON
- Codis 3.0.1 安裝使用文件(官方)
- 使用 OpenCV 進行文件矯正OpenCV
- 使用開源文件工具docsify,用寫部落格的姿勢寫文件
- Flv.js文件使用隨記JS
- 使用 VS Code + Markdown 編寫 PDF 文件
- 使用工具生成 Protocol 易讀文件Protocol
- 使用halo快速搭建應用文件中心
- 使用 YApi 管理 API 文件,測試, mockAPIMock
- 使用Swashbuckle構建RESTful風格文件REST
- 使用PLSQL Developer 來檢視官方文件SQLDeveloper
- 使用Digester解析XML文件示例 (轉)XML
- 【Python】生成html文件-使用dominatePythonHTML
- ApiBoot - ApiBoot Resource Load 使用文件APIboot
- ExoPlayer的使用與解析(官方文件翻譯)
- .Net Api 之如何使用Elasticsearch儲存文件APIElasticsearch
- znai: 使用Markdown編寫Java文件系統AIJava
- 使用Java poi編輯word.docx文件Java
- 使用apiDoc書寫API文件規範API
- 使用Mkdocs構建你的專案文件
- 使用GhostDoc為程式碼生成註釋文件
- SVN使用技巧和參考文件總結
- SAP使用者入門參考文件
- knife4j api文件使用說明API