5

I'm trying to write some tests for fragments which have fields annotated with @Inject. For example, a chunk of my app looks like this:

Module:

@Module
public class PdfFactoryModule {

    @Provides @Singleton
    PdfFactory providePdfFactory() {
        return PdfFactory.getPdfFactory();
    }
}

Component:

@Singleton
@Component(modules = PdfFactoryModule.class)
public interface CorePdfComponent {
    void inject(PagerFragment pagerFragment);
}

Application:

public class CorePdfApplication extends Application {
    @NonNull
    private CorePdfComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerCorePdfComponent.builder().build();
    }

    @NonNull
    public CorePdfComponent getComponent() {
        return component;
    }

}

PagerFragment:

public class PagerFragment extends Fragment {
@Inject PdfFactory pdfFactory;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //Dagger 2
    ((CorePdfApplication) getActivity().getApplication()).getComponent().inject(this);
}

(Note that these are only snippets of my whole code, I'm showing only the essentials for this particular dependency to keep it clear.)


I was trying to do a test like this:

Fake Module:

@Module
public class FakePdfFactoryModule extends PdfFactoryModule {

    @Override
    PdfFactory providePdfFactory() {
        return Mockito.mock(PdfFactory.class);
    }
}

Fake Component:

@Singleton
@Component(modules = FakePdfFactoryModule.class)
public interface FakeCorePdfComponent extends CorePdfComponent {
    void inject(PagerFragmentTest pagerFragmentTest);
}

Fake Application:

public class FakeCorePdfApplication extends CorePdfApplication {
    @NonNull
    private FakeCorePdfComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerFakeCorePdfComponent.builder().build();
    }

    @NonNull
    public FakeCorePdfComponent getComponent() {
        return component;
    }
}

Test:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = FakeCorePdfApplication.class)
public class PagerFragmentTest {

    PagerFragment pagerFragment;

    @Before
    public void setup() {
        pagerFragment = new PagerFragment();
        startVisibleFragment(pagerFragment);
    }

    @Test
    public void exists() throws Exception {
        assertNotNull(pagerFragment);
    }

But the DaggerFakeCorePdfComponent doesn't generate. I may have messed up big time because I never tested with dependency injection. What am I doing wrong?

Haruspik
  • 412
  • 5
  • 17
  • If you're compiling with Dagger on the classpath, the failure to create DaggerFakeCorePdfComponent should come with an error, in your IDE's "Output" tab or your compiler's error log. Can you find any output like that? (Separately, I can't see where you're using Dagger in your test; why do you want the test accessible on the Component, as opposed to getting your system-under-test or dependency from a freestanding component?) – Jeff Bowman Sep 12 '16 at 19:13
  • It just says `Error:(14, 21) error: cannot find symbol variable DaggerFakeCorePdfComponent` As for the tests I would like to test the fragment itself but it will not run without the dependencies. – Haruspik Sep 12 '16 at 20:19
  • A bit of a shot in the dark, but my guess is that robolectric is actually not running the correct gradle tasks to run the ``apt`` plugin and generate the classes for you. Have you tried adding ``testCompile `` in addition to ``apt``? If not can you give it a shot and let me know here pls. – Fred Sep 13 '16 at 07:32
  • My dependency looks like this: `testCompile 'org.hamcrest:hamcrest-core:1.1' testCompile 'org.hamcrest:hamcrest-library:1.1' testCompile 'org.hamcrest:hamcrest-integration:1.1' testCompile "org.robolectric:robolectric:3.1.1" testCompile "org.robolectric:shadows-support-v4:3.1.1" compile 'com.google.dagger:dagger:2.6.1' apt "com.google.dagger:dagger-compiler:2.6.1"` Do you want me to add `testcompile "com.google.dagger:dagger-compiler:2.6.1"` at the end? It gives me `Error:(50, 0) Could not find method testcompile() for arguments [com.google.dagger:dagger-compiler:2.6.1]` – Haruspik Sep 13 '16 at 09:10
  • I know you already accepted the answer, but it was suppose to be ``testCompile`` with capital ``C`` :) – Fred Sep 14 '16 at 10:31

2 Answers2

10

My advice - "Do not use dagger in tests".

Just change your code to next:

public class FakeCorePdfApplication extends CorePdfApplication {
    @NonNull
    private CorePdfComponent component = mock(CorePdfComponent.class);

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

    @NonNull
    public CorePdfComponent getComponent() {
        return component;
    }
}

And:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = FakeCorePdfApplication.class)
public class PagerFragmentTest {

    PagerFragment pagerFragment;

    @Before
    public void setup() {
        pagerFragment = new PagerFragment();
        CorePdfComponent component = ((CorePdfApplication)RuntimeEnvironment.application).getComponent();

        doAnswer( new Answer() {
          Object answer(InvocationOnMock invocation) {
           fragment. pdfFactory = mock(PdfFactory.class);
           return null;
          }
        }).when(component).inject(pageFragment);
        startVisibleFragment(pagerFragment);
    }

    @Test
    public void exists() throws Exception {
        assertNotNull(pagerFragment);
    }
}
Eugen Martynov
  • 19,888
  • 10
  • 61
  • 114
1

You may try:

androidTestApt "com.google.dagger:dagger-compiler:<version>"

I was having similar problem, it worked for me.

Abdul Wadood
  • 1,161
  • 1
  • 10
  • 18