In the last tutorial we created a SubComponent to insert NetworkApi into an activity
Now if we use a UI testing frameworks for e.g. espresso, we know that we

need to mock a lot of things that interact with external system.
NetworkApi is one of the call that needs to be mocked because we cannot make actual network call in Api when doing just UI testing.
Mocking SubComponent is slightly more difficult than mocking Components.
Let’s see how to do that.
First we create a test , where we will call espresso methods to check the inputs etc.
<br data-mce-bogus="1"> <pre>/** * Created by AKS on 10/18/2017. */ @RunWith(AndroidJUnit4.class) @CustomScope public class MockActivityTest { @Inject NetworkApi networkApi; @Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>( MainActivity.class, true, // initialTouchMode false); // launchActivity. False so we can customize the intent per test method @Before public void setUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); CustomApplication app = (CustomApplication) instrumentation.getTargetContext().getApplicationContext(); MockSubAppComponent component = (MockSubAppComponent) app.getAppSubComponent(); component.inject(this); } @Test public void testThis(){ activityRule.launchActivity(new Intent()); } }</pre>
There are a few important points . We need to inject the mock into our test class
@Inject NetworkApi networkApi;
Next, we create a rule where we do not start activity automatically
@Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>( MainActivity.class, true, // initialTouchMode false); // launchActivity. False so we can customize the intent per test method
And we leave out the setup for now .
Let us instead focus on module that will be used to provide mock NetworkApi
@Module public class MockNetworkApiModule { @CustomScope @Provides NetworkApi provideNetworkApi(){ return mock(NetworkApi.class); } }
The MockAppComponent will subclass AppComponent because where ever we can inject base class, we can safely inject subclass too
@Component public interface MockAppComponent extends AppComponent { MockSubAppComponent.Builder mockSubComponentBuilder(); }
The MockComponent looks similar to AppComponent
Then we have the MockSubComponent which extends SubComponent
@CustomScope @Subcomponent(modules = {MockNetworkApiModule.class}) public interface MockSubAppComponent extends AppSubComponent{ public void inject(MockActivityTest mainActivity); @Subcomponent.Builder public interface Builder{ MockSubAppComponent subComponent(); } }
We create the MockApplication class which subclasses CustomApplication class
public class MockApplication extends CustomApplication { @Override protected MockSubAppComponent createSubComponent() { return DaggerMockAppComponent .builder() .build() .mockSubComponentBuilder() .subComponent(); } }
and here you can see how I built the mock subcomponent ( actually not very different than SubComponent construction.
Back to activity test method
@Before public void setUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); CustomApplication app = (CustomApplication) instrumentation.getTargetContext().getApplicationContext(); MockSubAppComponent component = (MockSubAppComponent) app.getAppSubComponent(); component.inject(this); }
We just get the MockApplication ( we will see how ) and then we use it to get subcomponent
Now how do we get MockApplication instead of CustomApplication. We create an object called as MockTestRunner.
public class MockTestRunner extends AndroidJUnitRunner { @Override public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return super.newApplication(cl, MockApplication.class.getName(), context); } }
This runner class will provide us with MockApplication class .
But how do we cause Android to use this runner for testing.
Open build.gradle and
change the parameter for runner as follows
defaultConfig { applicationId "com.instanect.testdaggersubcomponent" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "com.instanect.testdaggersubcomponent.MockTestRunner" }
make sure that you use the right package name .
That’s it !!
Delete any original configuration you have created because it does not pick up our runner. Run with new configuration.
Don’t forget to use same @CustomScope wherever relevant otherwise you will receive different objects in mocking and in actual activity.
Now write your espresso code knowing that you have successfully mocked Dagger SubComponent in your work 🙂