Search code examples
androidandroid-espressodagger-2mockwebserver

How to test screens which requires authorization? Espresso + MockWebServer


I am creating UI tests. In order not to interact with the real server, I use MockWebServer. My goal is to emulate various server responses and see how the program as a whole will respond to them. At the moment, I don’t understand how to open screens that require authorization. Of course, I can write a code that will login to the authorization screen, and then go to the desired window. But this requires additional time to complete the test, and I would like to avoid this. I would not want to mocking classes, because I need to check the production version of the application. How can i do this?

For DI, I use Dagger-2. Here is the component code:

@Singleton
@Component(modules = {
        AvatarsModule.class,
        EncryptionModule.class,
        ApiModule.class,
        WalletsModule.class,
        GeneralModule.class,
        InteractorsModule.class,
        PushNotificationsModule.class,
        AppModule.class
})
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder context(Context context);
        AppComponent build();
    }

    void inject(App app);
}

Here is the class code in which the authorization state is stored:

public class ApiWrapper {
    private Api api;
    private KeyPair keyPair;
    private Account account; 

    ...

    public Flowable<Authorization> authorize(KeyPair tempKeyPair) {
        return api
                .authorize(tempKeyPair.getPublicKeyString().toLowerCase())
                .subscribeOn(Schedulers.io())
                .doOnNext((authorization -> {
                    this.account = authorization.getAccount();
                    this.keyPair = tempKeyPair;
                }));
    }
    ...
}

Solution

  • If anyone is still interested. I wrote an InstrumentationTestFacade class in which I put an ApiWrapper object using Dagger. Next, the InstrumentationTestFacade is injected into the Application object. Since the application object is not a singleton, there is no leak of responsibility in the main code, but from the test code you can access this facade using the following code:

    Application application = (Application) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
    InstrumentationTestFacade facade = application.getInstrumentationTestFacade();
    

    Below is an example:

    public class InstrumentationTestFacade {
        private LogoutInteractor logoutInteractor;
        private SecurityInteractor securityInteractor;
        private ApiWrapper apiWrapper;
    
        public InstrumentationTestFacade(
                LogoutInteractor logoutInteractor,
                SecurityInteractor securityInteractor,
                ApiWrapper apiWrapper
        ) {
            this.logoutInteractor = logoutInteractor;
            this.securityInteractor = securityInteractor;
            this.apiWrapper = apiWrapper;
        }
    
        public void logout() {
            logoutInteractor.logout();
        }
    
        public ApiWrapper getApiWrapper() {
            return apiWrapper;
        }
    
        public SecurityInteractor getSecurityInteractor() {
            return this.securityInteractor;
        }
    }
    
    public class Application extends MultiDexApplication implements HasActivityInjector, HasServiceInjector {
    
        ...
    
        @Inject
        InstrumentationTestFacade instrumentationTestFacade;
    
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            DaggerAppComponent
                    .builder()
                    .context(this)
                    .build()
                    .inject(this);
    
        }
    
        ...
    
        public InstrumentationTestFacade getInstrumentationTestFacade() {
            return instrumentationTestFacade;
        }
    }