73
votes

I am following the guide here: https://github.com/ecgreb/dagger-2-testing-demo

I have the following setup in my app/src/main (the injection and @Provides code omitted):

public class FlingyApplication extends Application {
    @Singleton
    @Component(modules = { FlingyModule.class })
    public interface FlingyComponent
}

@Module
public class FlingyModule

In app/src/test:

public class TestFlingyApplication extends Application {
    @Singleton
    @Component(modules = { TestFlingyModule.class })
    public interface TestFlingyComponent extends FlingyComponent
}

@Module
public class TestFlingyModule

So far, it is nearly identical to the example github. When dagger goes to generate the code for the Component builders in src/main, they generate properly. Dagger does not, however, generate code for the Component builders in src/test.

My main build.gradle:

dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0-alpha3'

    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.5.1'
}

My app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'


android {
    # There is obviously more in here, but this is the custom part:
    packagingOptions {
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
}

dependencies {
    compile 'com.squareup:otto:1.3.8'
    compile 'com.android.support:cardview-v7:23.1.1'
    compile 'com.android.support:recyclerview-v7:23.1.1'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
    compile 'com.squareup.picasso:picasso:2.5.2'
    compile 'com.jakewharton:butterknife:7.0.1'

    compile 'com.google.dagger:dagger:2.0.1'
    apt 'com.google.dagger:dagger-compiler:2.0.1'
    compile 'javax.annotation:javax.annotation-api:1.2'

    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'

    testCompile 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:3.0'
    testCompile 'org.mockito:mockito-core:1.10.19'
}

So when I build, I get the DaggerFlingyApplication_FlingyComponent class, but not the DaggerTestFlingyApplication_TestFlingyComponent

Something interesting I noticed is that if I switch the line:

apt 'com.google.dagger:dagger-compiler:2.0.1'
# TO
compile 'com.google.dagger:dagger-compiler:2.0.1'

I see the following when I run ./gradlew compileDebugUnitTestSources:

:app:compileDebugJavaWithJavac
Note: /app/build/generated/source/apt/debug/com/jy/flingy/DaggerFlingyApplication_FlingyComponent.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:app:preDebugUnitTestBuild UP-TO-DATE
:app:prepareDebugUnitTestDependencies
:app:compileDebugUnitTestJavaWithJavac
Note: /app/build/intermediates/classes/test/debug/com/jy/flingy/DaggerTestFlingyApplication_TestFlingyComponent.java uses unchecked or unsafe operations.

I don't know why it builds to intermediates and I assume that I need the build.gradle file to use apt instead of compile, but I can't seem to figure out how to get this to work. I know that it's absolutely possible.

8
This documentation (google.github.io/dagger/testing.html) advices not to use dagger for unit testing..Kishan B
If you have a lot of dependencies, then it could point to your unit tests testing more than a single unit. That said, google doesn't always know best (gasp!) and that's a pretty broad statement by them. Using dagger in unit tests in particular cases works well for us, hence this question.jyanks

8 Answers

139
votes

You need to add following to your build.gradle file for instrumentation test:

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

or for JUnit test:

testApt 'com.google.dagger:dagger-compiler:<version>'

This is required to generate Dagger code for your test components.


EDIT:

If you are using jack tool chain then add following for android test:

androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:<version>'

for JUnit tests:

testAnnotationProcessor 'com.google.dagger:dagger-compiler:<version>'

EDIT:

In case you are using kotlin-kapt for Kotlin code use following:

kaptAndroidTest 'com.google.dagger:dagger-compiler:<version>'

or for JUnit test:

kaptTest 'com.google.dagger:dagger-compiler:<version>'

Check this link for more info.

33
votes

For Android Studio 3 and dagger 2.13 the already mentioned annotation processors are needed:

testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.13'

But also do not forgot to do this for the instrumented test under androidTest:

androidTestAnnotationProcessor'com.google.dagger:dagger-compiler:2.13'

You might get the impression that this alone does not work, because the DaggerXYZ classes are not generated. After hours I found out that the test source generation is only triggered when the tests are executed. If you start a test or androidTest from Android Studio the source generation should be triggered.

If you need this earlier trigger gradle manually:

gradlew <moduledirectory>:compile<Flavor>DebugAndroidTestSources
gradlew <moduledirectory>:compile<Flavor>DebugTestSources

Replace Debug if you run a test in a different build type.

Note:

If you are using multiDexEnable = true you might get an error:

Test running failed: Instrumentation run failed due to 'java.lang.IncompatibleClassChangeError'

Use a different runner in this case:

android {

  defaultConfig {
    multiDexEnabled true
    testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
29
votes

Just to add a bit to the above answer, since there have been some recent changes.

From Android Gradle plugin version 2.2 and above you will no longer use testApt.

So from now on you need to put only this in the build.gradle:

testAnnotationProcessor 'com.google.dagger:dagger-compiler:<version>'

But more than that, what I came here for, is the following: if you need gradle to generate the DaggerComponent classes for you you will have to do a bit extra work.

Open our build.gradle file and AFTER the android section write this:

android.applicationVariants.all { variant ->
    if (variant.buildType.name == "debug") {
        def aptOutputDir = new File(buildDir, "generated/source/apt/${variant.unitTestVariant.dirName}")
        variant.unitTestVariant.addJavaSourceFoldersToModel(aptOutputDir)
        assembleDebug.finalizedBy('assembleDebugUnitTest')
    }
}

This will create the directory build/generated/source/apt/test/ as a Java classes recipient and the last part will trigger the "assembleDebugUnitTest" task that will finally create those Dagger2 components in the folder that was just created.

Note that this script is just being triggered for the "debug" variant and takes advantage of that build variant using the "assembleDebug" task. If for some reason you need it in other variants just tweak that a bit.

Why Dagger2 does not do this automatically is beyond me, but hey, I am no pro.

16
votes

If you added kaptAndroidTest for dagger dependencies and still not getting test components when rebuild your project, try running assembleAndroidTest.

2
votes

Adding to the above solution and adding the testKapt and androidTestKapt for dagger, I had the problem that my modules and components had the wrong imports as a result of missing imports

e.g

 import android.support.test.espresso.core.deps.dagger.Module
 import android.support.test.espresso.core.deps.dagger.Module

instead of

import dagger.Module
import dagger.Provides

Hope this helps

1
votes

Hi even after adding all gradle dependenices and annotations if it still doesnt work then you need to run assembleAndroidTest gradle script for this. Simply make an empty test case and run it. It will do the job for you. Cheers

0
votes

If you are using kotlin use "kaptAndroidTest" to generate dagger component for android tests in your build.gradle file.

0
votes

I ran ./gradlew build from the command line and got information about a missing Provides method that Android Studio was not telling me about.