Excluding Dagger 2 generated classes from test coverage - android

I am using Dagger 2 library to mock dependencies. While running unit test cases with code coverage it considers these generated dependencies and not the classes used for class under the question.
Is it possible to exclude these classes?
What I tried
Run -> Edit config -> Test coverage -> Exclude Package
Individually adding generated class files, but it doesn't work
Any help here?

Related

Modularised Android testing - module test using base module test

I have a android project with multiple modules, module B that depends on module A
dependencies {
implementation project(':features:A')
}
I want unit test in module B to extend base test from module A. An IDE resolved all dependencies successfully, but in build time only production code from module A is accessable in test.
I tried to add dependency specificly for tests:
dependencies {
testCompile project(':features:A').sourceSets.test.output
}
This results an error:
Could not get unknown property 'test' for SourceSet container of type org.gradle.api.internal.tasks.DefaultSourceSetContainer.
Same if I'm trying to create custom artifact with tests.
How can I get access to test-specific code in base module from dependent module?
AS 3.1, gradle 4.1
The best solution I found is to create third module just for tests, put my base test classes and test dependencies there.
And use it in both module A and module B (and all other modules - just one module for all unit test dependencies)
testImplementation project(':features:basetests')
Disadvantage - it is possible to import it in non-test artifacts (e.q. implementation project(':features:basetests')). Good code review can protect from such mistakes.
Looks like there is no convenient way to define base shared test-only code.

Using Dagger2 with flavors and androidTest source set

I am attempting to write instrumentation tests for an app that uses Dagger2. The app has several build types and one of them is called automation. The automation build type is also set as the testBuildType in the build.gradle.
The activity I'd like to test has a Presenter injected that depends on a Repository. What I'd like to do is inject a real repository when I'm building any build type except for automation and then when I build the automation build variant I'd like to inject a mock Repository.
In my androidTest source set I've created a MockComponent and a MockModule which create mock a mock version of the Repository. What I figured I would be able to do is build an instance of MockComponent and use it to provide a Mock Repository. The problem seems to be that if MockComponent and MockModule are in the androidTest source set then Dagger never generates the DaggerMockComponent class when I build. If I move MockComponent and MockModule to the automation source set then Dagger does generate the classes however then I'm in a position where I want to add an inject method to MockComponent so that I can inject an object into my activity test class but MockComponent can't get see the activity test class because it's in the android source set.
Any ideas why Dagger won't generate the DaggerMockComponent class when it's in the androidTest source set?
The issue ended up being that in my build.gradle file I needed to include a dependency for the androidTest source set. Normally you use
androidTestCompile "com.package.version:1.2.3"
But for dagger you have to use apt so the line looks like:
androidTestApt "com.google.dagger:dagger-compiler:$rootProject.ext.daggerVersion"
The really confusing thing about this was that since I am using a build type (automation) for my testBuildType I was able to add dagger code to the automation source set and dagger could compile it b/c it wasn't in the androidTest source set. Then I tried to add the dagger code to the androidTest source set and suddenly it was like it was invisible to the dagger compiler b/c it would never generate the dagger classes for the components that were specified in the androidTest source set.

How to exclude Dagger2 classes from test coverage

Is there any option to exclude Dagger2 classes from test coverage report in Android Studio
JaCoCo excludes
If you're using JaCoCo, for example using android instrumentation connected tests, you need to configure the excludes (or includes), which, according to the documentation is...
A list of class files to exclude from the report. May use wildcard characters (* and ?). When not specified nothing will be excluded.
Which means you need to match the generated dagger class names. The following rules cover virtually any class generated by dagger-compiler, without matching any of non-generated classes (unless you name your class the same as dagger does...):
excludes = [
'**/*_MembersInjector.class',
'**/Dagger*Component.class', // covers component implementations
'**/Dagger*Component$Builder.class', // covers component builders
'**/*Module_*Factory.class'
]
You can check your generated dagger classes in app/build/generated/source/apt directory after running a build, to see if there are any additional generated classes that you want to match with excludes.
This excludes array is a configuration property of jacoco plugin. Now, where to put this excludes array depends on whether you define your own tasks based on the jacoco plugin, or use a 'higher level plugin' that does this for you. For example using this plugin (you can see the plugin source to see where the excludes are actually applied):
jacocoAndroidUnitTestReport {
excludes += [
'**/*_MembersInjector.class',
'**/Dagger*Component.class',
'**/Dagger*Component$Builder.class',
'**/*Module_*Factory.class'
]
}
Connected tests
If you're running android connected test coverage by setting testCoverageEnabled true in your buildType, unfortunately there is no idiomatic way to declare excludes, since the android gradle plugin doesn't provide such options, and the predefined jacoco report task has the excludes hardcoded. In this case, you have to script your own task with excludes.
IntelliJ test runner
If you're using the IntelliJ test runner, whether the coverage is done by IntelliJ or JaCoCo, you need to put the includes for a test configuration.
Open the Edit Configurations window:
Choose your test config and define includes (classes or whole packages). In this case I included the whole com.google.android.gms package, just as an example:
To exclude dagger generated files, the quickest way is to put all the dagger dependencies in one root package, and include all the other packages in the test configuration.
Exclude files from AndroidStudio index
After many days I found solution: exclusion files from IDE index also exclude them from IDE's code coverage report.
So we need create new File-Type for all codegen files (or only Dagger/Hilt files), and exclude this File-Type from index.
To achieve this you need:
1. Create new File-Type 'Codegen' for codegen-files
Go to Preferences -> File Types, and add new file-type Codegen:
Add this templates (templates may change in future):
*_*Factory.java
*_ComponentTreeDeps.java
*_Factory.java
*_GeneratedInjector.java
*_HiltComponents.java
*_HiltModules.java
*_HiltModules_BindsModule.java
*_HiltModules_KeyModule.java
*_MembersInjector.java
*_ProvideFactory.java
*_SingletonC.java
*_TestComponentDataSupplier.java
BR.java
BuildConfig.java
DataBinderMapperImpl.java
Hilt_*.java
_test_*.java
2. Exclude 'Codegen' from index
Open Go to File, choose tab Files and then click Filter and uncheck Codegen file-type.
That's all!
After this when you will run tests with code coverage (with IntelliJ IDEA runner), matched Codegen-files will be excluded also from code coverage report in IDE.
Note: With this approach you can't get coverage from CLI. If you need get coverage from CLI - use Jacoco.
More recent versions of Dagger generate additional files with slightly different patterns. I had success with the following excludes when using Dagger 2.15
'**/*_MembersInjector.class',
'**/Dagger*Component*.class',
'**/Dagger*Subcomponent*.class',
'**/*Subcomponent$Builder.class',
'**/*Module_*Factory.class',

Gradle Android - Test coverage on legacy test projects structure

I am migrating a quite big and complex legacy project to Gradle.
I have many libraries projects, where each project has its own test subprojects.
The project structure looks as follows:
- LibraryX
-- LibraryXTestsOne
-- LibraryXTestsTwo
- LibraryY
-- LibraryYTests
....
Each library is configured as follows:
The main sourceset contains all the library code
The androidTest sourceset is empty
Each test project is configured as follows:
It lists the parent library as a dependency
The main sourceset is empty
The androidTest sourceset contains all the tests code
With such a configuration I manage to run the test with :libraryTestsA:connectedAndroidTest, but I can't get the test coverage to work. I enable it through
buildTypes{
debug{ testCoverageEnabled true }
}
Apparently the classes of the parent library are not instrumented causing the code coverage to be zero.
Any suggestions?
It turns out that you just need to set testCoverageEnabled to true for all the projects/libraries that the test project depends on.
That will be enough to instrument your code.
You still need to create a custom jacocoReport task though, since the one provided by the android plugin looks up classes and sources in the project main sourceset, which are empty in a configuration like mine.

Android testCompile project not working

I am using Robolectric and the "android-unit-test" plugin in my android project. I have multiple modules (eg foo and bar) and would like to use the same test runner across all the modules. To achieve this I created a Test module to hold my test runner and any custom shadow classes. In foo's build.gradle I have testCompile project (":main:Test")however android studio will not import my runner unless I also include androidTestCompile (":main:test"). Furthermore, when I run ./gradlew test from the command line I get various errors saying that my test runner and shadow classes cannot be found. Anyone have any advice?
With your given information I guess you missed one little aspect: test classes appear not in jars.
When your shared TestRunner is under src/test/java then other modules will not see it. When I'm correct then place you TestRunner at src/main/java and all modules should have access to our support classes.
Here is also an example where i did the same https://github.com/nenick/AndroidAppDevelopment

Categories

Resources