0

I understand how Dagger2 works,

I understand it allows to easily swap dependencies, so we can use mocks for testing.

Point is that I am not sure I understand how am I supposed to provide different Dagger2 Components implementations for testing and for debug/production.

Would I need to create 2 Gradle productFlavors (e.g "Production"/"Test") that would contain 2 different Components definition?

Or can I specify that I want to use the mock Component for test compile and the non mock Component for non test builds?

I am confused, please some clarification would be great!

Thanks a lot!

Lisa Anne
  • 4,482
  • 17
  • 83
  • 157
  • As you know dagger has provides methods that pass in objects on which your class depends on, put a flag in the provides method to check if a static variable `isTest` is true of false, if true then return mock objects – Bhargav Mar 10 '16 at 10:12
  • yes, that is an option, but ugly isn't it ?:-( – Lisa Anne Mar 10 '16 at 10:28
  • It is ugly there are better solutions try looking at famous open source android apps maybe you can get something from them – Bhargav Mar 10 '16 at 10:45

1 Answers1

2

Unit testing

Don’t use Dagger for unit testing

For testing a class with @Inject annotated constructor you don't need dagger. Instead create an instance using the constructor with fake or mock dependencies.

final class ThingDoer {
  private final ThingGetter getter;
  private final ThingPutter putter;

  @Inject ThingDoer(ThingGetter getter, ThingPutter putter) {
    this.getter = getter;
    this.putter = putter;
  }

  String doTheThing(int howManyTimes) { /* … */ }
}

public class ThingDoerTest {
  @Test
  public void testDoTheThing() {
    ThingDoer doer = new ThingDoer(fakeGetter, fakePutter);
    assertEquals("done", doer.doTheThing(5));
  }
}

Functional/integration/end-to-end testing

Functional/integration/end-to-end tests typically use the production application, but substitute fakes[^fakes-not-mocks] for persistence, backends, and auth systems, leaving the rest of the application to operate normally. That approach lends itself to having one (or maybe a small finite number) of test configurations, where the test configuration replaces some of the bindings in the prod configuration.

You have two options here:

Option 1: Override bindings by subclassing modules

    @Component(modules = {AuthModule.class, /* … */})
    interface MyApplicationComponent { /* … */ }

    @Module
    class AuthModule {
      @Provides AuthManager authManager(AuthManagerImpl impl) {
        return impl;
      }
    }

    class FakeAuthModule extends AuthModule {
      @Override
      AuthManager authManager(AuthManagerImpl impl) {
        return new FakeAuthManager();
      }
    }

    MyApplicationComponent testingComponent = DaggerMyApplicationComponent.builder()
        .authModule(new FakeAuthModule())
        .build();

Option 2: Separate component configurations

@Component(modules = {
  OAuthModule.class, // real auth
  FooServiceModule.class, // real backend
  OtherApplicationModule.class,
  /* … */ })
interface ProductionComponent {
  Server server();
}

@Component(modules = {
  FakeAuthModule.class, // fake auth
  FakeFooServiceModule.class, // fake backend
  OtherApplicationModule.class,
  /* … */})
interface TestComponent extends ProductionComponent {
  FakeAuthManager fakeAuthManager();
  FakeFooService fakeFooService();
}

More about it in the official documentation testing page.

LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56