Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android espresso - how to use dagger2 to inject dependencies

Injecting dependencies into espresso test using dagger2 is what i would like to do.

I would like a way to be able to provide dependencies for my test cases using dagger.

In particular there is a MockwebServer class i want to inject with dagger. How is this done? My project already has dagger set up. its a single component for now and the single componeent has 5 modules which look like this:

@Singleton
@Component(modules = {AppModule.class, NetworkModule.class, RepositoryModule.class, UseCaseModule.class, ActivityModule.class, PresenterModule.class})
public interface AppComponent {

    void inject(NetworkSessionManager target);
    void inject(SplashActivity target);
    void inject(AuthenticationActivity target);
    void inject(WelcomeActivity target);
    void inject(LoginFragment target);
}

AND IT WORKS FINE. But now when i move to the androidTest folder to do an espresso test how would i use the following component:

    //note the NetworkTestModule.class i want to use is defined instead of //networkModule.class
        @Singleton
        @Component(modules = {AppModule.class, NetworkTestModule.class, RepositoryModule.class, UseCaseModule.class, ActivityModule.class, PresenterModule.class})
        public interface AppTestComponent

 {

        void inject(NetworkSessionManager target);
        void inject(SplashActivity target);
        void inject(AuthenticationActivity target);
        void inject(WelcomeActivity target);
        void inject(LoginFragment target);
        void inject (MYTESTCLASS target);
    }

what i have been doing it keeping the AppTestComponent in the main source code but it cant see MYTESTCLASS this way ?

The reason i want to inject into my class, is that i want to inject a mockWebServer class after passing it to retrofit as the baseurl like this:

TestNetworkModule.java:

@Provides
@Singleton
public Retrofit provideRetrofit(Converter.Factory converter, OkHttpClient client, @Named(BASE_URL) String baseUrl, MockWebServer server) {
    return new Retrofit.Builder()
            .baseUrl(server.url("/").toString())
            .client(client)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(converter)
            .build();
}

    @Singleton
    @Provides
    MockWebServer providerMockWebServer() {
        return new MockWebServer();
    }
    //....
    }

this way i can get a reference of the MockWebServer and use it in my tests and have retrofit work with it so i can do fast integration tests

also in gradle i am using the following dependencies please confirm:

 compile 'com.google.dagger:dagger:2.9'
testCompile 'com.google.dagger:dagger:2.9'
annotationProcessor 'com.google.dagger:dagger-compiler:2.9'
like image 891
j2emanue Avatar asked Dec 19 '22 06:12

j2emanue


1 Answers

I will try to explain how do it:

In your application class you should have specified Application component which contains your API service:

protected AppComponent initializeAppComponent() {
    return DaggerAppComponent.builder()
            .apiServiceModule(new APIServiceModule(this))
            .otherModules(new otherModules(this))
            .build();
}

And inject like this:

@Override
public void onCreate() {
    applicationComponent = initializeAppComponent();
    applicationComponent.inject(this)}};

It is standard initialization. You need to just move component builder to method which can be overrided later.

In your android Tests package:

Now, you need to create new Application class which extends your application class where you have your dagger component initialization.

So now you can override initializeAppComponent() method and switch your APIServiceModule by the new one which extends previous Module. It should looks like this:

public class MockApp extends App {

@Override
protected AppComponent initializeAppComponent() {
    return DaggerAppComponent.builder()
            .apiServiceModule(new MockAPIServiceModule(this))
            .otherModules(new OtherModules(this))
            .build();
}

@Module
private class MockAPIServiceModule extends APIServiceModule {

    @Override
    public ApiService provideApiService(@Nonnull final Retrofit retrofit,
                                        @Nonnull final Gson gson) {
        return new ApiService() {
            @Override
            public Observable<LoginResponse> login(@Body final LoginRequest loginRequest) {
                return // what you want to
            }
        }
    }
}}

You need to declare which Application class should be used for tests. Now you need to do 2 more things: 1. Create new runner which pointing on new App Class

public class MockTestRunner extends AndroidJUnitRunner{

@Override
public void onCreate(Bundle arguments) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().build());
    super.onCreate(arguments);
}
@Override
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return super.newApplication(cl, MockApp.class.getName(), context);
}}
  1. Declare which runner will be used in build.gradle.

testInstrumentationRunner ".MockTestRunner"

Thats it. Now your requests will use mocked responses! If you have any question just ask.

Cheers

like image 95
Piotr Mądry Avatar answered Mar 16 '23 02:03

Piotr Mądry