Using Dagger to mock subcomponent – Part 1

Dagger-Courtsey pixabay.com
Dagger

Dagger is a library that makes testing of code much possible. But Dagger can be hard to learn and difficult to wrap around your head even if the concept of Dagger revolves around Builder pattern and dependency injection.

This tutorial is not for learning Dagger but making a testable system ( for e.g. testing of UI with espresso) where you can inject mocks using Subcomponents.

What are subcomponents ?

Subcomponents are those components that are created to append modules to object graph created by main component using which we inject our activity with variables that we need to perform operations as well as mock with.

So if we need a variable that is needed application wide , we can put that in main component. But if need a few parameters that are only specific to an activity we can extend the object graph by declaring a Subcomponent and making sure that scope of that SubComponent is restricted the activity.

This post heavily borrows from and is inspired by Dagger 2 + Espresso 2 + Mockito 

This post shows how to create SubComponents , inject parameters from them into Activity and mock them in Instrumentation testing.

To start with we create a basic activity and create a parameter network Api to it


@CustomScope
public class MainActivity extends AppCompatActivity {

@Inject
NetworkApi networkApi;

Using @Inject we are telling Dagger that we want this variable to be set when activity is started. Maybe we will use it to make network calls during the lifecycle of activity

Now to fill it we have three pieces in our puzzle

An Application object that is available to all activities upon creation

A Module to provide real values to this variable when activity is started

and a component that takes data from the module and passes it to activity

( Dagger to that extent is quite simple 😉 )

So we create a Custom Application Object


public class CustomApplication extends Application {

Now we need to somehow tell the Android app to use this Custom Application when the app is started.

We can easily tell that by adding the line in AndroidManifest.xml


<application
 android:name=".CustomApplication"

Now the CustomApplication Object will be used in place of standard Application Object

Next

Creating a module

We know that we need to get the actual object for networkApi parameter to be instantiated somewhere. We do so in a class called as NetworkApiModule and to let Dagger know that it needs to use this module to provide the value we annotate it with @Module annotation


@Module
 public class NetworkApiModule {

@Provides
 NetworkApi provideNetworkApi(){

return new NetworkApi();
 }
 }

The implementation details are kept at minimum

Now to make passage of this variable to activity

Add component


@Component
 public interface AppComponent {

AppSubComponent.Builder subComponentBuilder();

}

But this component is slightly different when you compare it with other examples of Dagger. The AppComponent uses a builder to inject the networkApi using a subcomponent because probably we do not need networkApi at global level ( for e.g. because we make calls in only certain actvities and not all ) .

We saw that there is a subcomponent class which is being used by AppComponet , we create it like this


@Subcomponent(modules = {NetworkApiModule.class})
 public interface AppSubComponent {

public void inject(MainActivity mainActivity);

@Subcomponent.Builder
 public interface Builder{

AppSubComponent subComponent();
 }
 }

A few points to note here.

The subcomponent class is annotated with @SubComponent annotation

It uses NetworkApiModule to get NetworkApi instance

We inject that into our activity

And since we know that we can only inject if we create a connection from AppComponent into SubComponent, we create a builder interface.

We annotate the interface with @Subcomponent.Builder. These annotations are instruction to dagger to help it connect with main component.

We know that it is not the AppComponent but SubComponent that provides that value , we need to connect AppComponent to SubComponent and that is thru the method AppSubComponent subComponent();

Once we get the SubComponent, we know we now can provide network Api object to our Activity.

You can annotate provides method with Singleton if the object that you are supplying to the activity does not need to be recreated every time.

But if that is not the case , we can use something called as Scope.

Now Scope can be used to determine what can and cannot be used for an activity. So if an activity does not need networkApi then it should not be injectible at all. So by using scope we restrict what can be provided to object we are injecting into.

So we create a custom scope ( you can have any name )


@Scope
 @Retention(RetentionPolicy.RUNTIME)
 public @interface CustomScope {
 }

And assign this scope to the activity



@CustomScope
 public class MainActivity extends AppCompatActivity {

@Inject
 NetworkApi networkApi;

@Override
 protected void onCreate(Bundle savedInstanceState) {

<br data-mce-bogus="1">

And also to subcomponent


@CustomScope
 @Subcomponent(modules = {NetworkApiModule.class})
 public interface AppSubComponent {

<br data-mce-bogus=”1″>

Now we have completed the connection (except for actual injection into activity)&nbsp; . Let us compile the program ( Build -&gt; Rebuild Project) .

If you look up at what has been created you can see that a Dagger Helper is created

DaggerAppComponent . IF you open the source code you will see that all the dependencies you created are part of the code .

Now we add a few things to CustomApplication and Activity classes to help inject the instantiated object into Activity.


public class CustomApplication extends Application {

AppSubComponent appSubComponent = createSubComponent();

protected AppSubComponent createSubComponent() {
 return DaggerAppComponent.builder().build().subComponentBuilder().subComponent();
 }

@Override
 public void onCreate() {
 super.onCreate();

}

public AppSubComponent getAppSubComponent() {
 return appSubComponent;
 }
 }

There are two important points

Create Subcomopnent only once and return it

Use generated DaggerAppComponent and build the subcomponent. The last call will chain with inject.

And in activity inject this like


@CustomScope
 public class MainActivity extends AppCompatActivity {

@Inject
 NetworkApi networkApi;

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

((CustomApplication)getApplication()).getAppSubComponent().inject(this);

}
 }

The network api object will now be injected into activity successfully.

In the next part of this tutorial, let us mock the subcomponent

Leave a Reply

Your email address will not be published. Required fields are marked *