Android - Connected Tests Results Directory - android

I have an android project where I run normal unit tests as well as instrumented tests.
Now the thing is that the results of the
unit tests are stored in build/reports/tests (./debug or ./release)
instrumented tests are stored in build/outputs/connected
Is it somehow possible to change the result directory of the instrumented tests to build/reports/tests/connected?
Thank you already!

Found the answer myself, To change the directory for instrumented tests use:
android {
testOptions {
reportDir = "$project.buildDir/reports"
resultsDir = "$project.buildDir/test-results"
}
# Or for lint if needed
lintOptions {
htmlOutput = file("$project.buildDir/reports/lint/LINT-results.html")
xmlOutput = file("$project.buildDir/test-results/lint/LINT-results.xml")
}
}

I believe this would be more reliable:
android {
[...]
testOptions {
if (project.hasProperty("customResultsDir")) {
resultsDir "${customResultsDir}"
}
if (project.hasProperty("customReportDir")) {
reportDir "${customReportDir}"
}
}
}
There is the bonus that it checks if the property is passed before trying to use it (a good practice). But the real interesting part is that this ways you are interpolating a closure instead of a variable - the differences allow further possibilities (check string interpolation and closure interpolation).
On my scenario, only by using the closure did I manage to have the properties to populate at the exact time.

Related

Sonar Jacoco not considering Kotlin androidTest (integration test case) in coverage

I have local and integrated test cases written for my android project. Using Kotlin(1.4.21) Robolectric(4.5.1), sonar(2.7.1), Jacoco(maven plugin 0.8.2)
The problem is that the Sonar and Jacoco is not considering androidTest(integration test case) written in Kotlin for code coverage
However sonar is showing correct coverage for other test cases like-
java unit test cases -> working
koltin unit test case -> working
java integrated test cases -> working
kotlin integrated test cases -> NOT WORKING
Although I have checked the paths I have set for sonar and it's all correct.
properties['sonar.java.binaries'] = files("${buildDir}/intermediates/javac/universalDebug/classes")
properties["sonar.java.binaries"] += files("${buildDir}/tmp/kotlin-classes/universalDebug/")
properties['sonar.java.test.binaries'] = files("${buildDir}/intermediates/javac/universalDebugAndroidTest/classes")
properties['sonar.java.test.binaries'] += files("${buildDir}/tmp/kotlin-classes/universalDebugAndroidTest/")
I have gone through other stackoverflow questions but didn't find same problem. So, I'm unable to find out the issue why sonar is not showing coverage for my integrated test cases written in Kotlin.
Thanks in Advance
UPDATE
within adroidTest folder > I have further 2 packages.
MyApplicationTest> src> com > pkgA
> pkgB
It's considering the Tests files present in pkgA but not the other. I have recently created this pkgB
What could be the possible reason for this? Do I have update some path somewhere?
You might need to do the following
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
Note that there are some issues with Java 11 that might fail your tests so you might want to also exclude jdk.internal as follows
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
excludes = ['jdk.internal.*']
}
Or a little bit verbose option but works:
subprojects {
pluginManager.withPlugin("com.android.library"){
android.testOptions.unitTests.all {
jacoco {
includeNoLocationClasses = true
excludes = ['jdk.internal.*']
}
}
}
pluginManager.withPlugin("com.android.application"){
android.testOptions.unitTests.all {
jacoco {
includeNoLocationClasses = true
excludes = ['jdk.internal.*']
}
}
}
apply plugin: 'jacoco'
}
I suggest you also upgrade your jacoco and sonar plugin versions if possible

Isolating test APK build

I have a situation where in an Android project with instrumentation tests I have all of the production code precompiled and ready to be installed as an .apk (a React Native environment).
Whenever I run instrumentation tests, I initially build the AndroidTest .apk using Gradle by running:
./gradlew assembleDebugAndroidTest -DtestBuildType=debug
(i.e. in a pretty standard way).
Trouble is that despite explicitly specifying only the xxxAndroidTest task, all of the production code assembly Gradle tasks are run as well. This is an extreme time waster to me since - as I explained, the production apk is already there, and thus code compilation (and packaging, signing, etc.) is scarce.
In essence, I have no dependency in production code from the instrumentation code -- even the ActivityTestRule I use is created dynamically and isn't directly bound to my main activity:
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(launchIntent, 0);
Class<?> activityClass = Class.forName(resolveInfo.activityInfo.name);
ActivityTestRule<?> activityTestRule = new ActivityTestRule(activityClass, false, false);
Question is: How can I isolate / restrict Gradle's work so it would only include test-related tasks? I even tried inspecting the tasks tree using this Gradle plugin, but couldn't find a clear place to "cut the tree" down.
Well so far I've come up with this (heuristic) solution, that does 2 things:
I noticed that most of the time that goes to waste is due to sub-projects that are not needed for the job. Therefore, the solution provides an easy way to exclude implementations from test building.
Out of the tasks remaining in the list, still - the plugin iteratively force-disables tasks that are not related but run nonetheless.
It boils down to this helper Gradle script:
// turbo-test-apk.gradle
def isEnabled = System.getProperty('TURBO_TEST_APK') != null
project.ext.dependenciesExcludeTest = { depsClosure ->
if (!isEnabled) {
dependencies(depsClosure)
}
}
gradle.taskGraph.whenReady { graph ->
if (isEnabled) {
def disabledTasks = new ArrayList<Task>(graph.allTasks.size())
[/.*JsAndAssets.*/, /package.*Release/, /package.*Debug/, /compile.*/, /.*[Pp]roguard.*/, /.*[Nn]ew[Rr]elic.*/, /.*AndroidTest.*/].forEach { regex ->
graph.allTasks.findAll { it.name ==~ regex }.forEach({ task ->
disabledTasks.add(task)
task.enabled = false
})
}
graph.allTasks.findAll { it.name ==~ /.*AndroidTest.*/ }.forEach({ task ->
task.enabled = true
})
println '--- Turbo test build: task scanning ---'
disabledTasks.forEach { task ->
if (!task.enabled) {
println 'Force-skipping ' + task
}
}
println '---------------------------------------'
}
}
Namely, the dependenciesExcludeTest enabled the exclusion of unwanted subprojects, and the task-graph-ready callback does the disabling. NOTE that the regex list is custom made, and is not generic. It makes sense for my project as react native projects have a heavy-weight JS-bundling tasks called bundleJsAndAssets, and I also have new relic installed. Nevertheless, this can be easily tailored to any project.
Also, the app.gradle looks something like this:
apply plugin: 'com.android.application'
apply from: './turbo-test-apk.gradle'
dependencies {
implementation "org.jetbrains.kotlin:$kotlin_stdlib:$kotlinVersion"
implementation "com.android.support:support-v4:$supportLibraryVersion"
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
// etc.
}
// These will be excluded when executing test-only mode
dependenciesExcludeTest {
implementation project(':#react-native-community_async-storage')
implementation project(':any-unneeded-sub-project')
}
So when gradle is run like this (i.e. with a custom TURBO_TEST_APK property):
./gradlew assembleDebugAndroidTest -DtestBuildType=debug -DTURBO_TEST_APK
the script will apply its work and reduce the overall build time.
This solution isn't optimal: tricky to maintain, doesn't omit all of the unnecessary work. I'd be very happy to see more effective solutions.

Gradle Espresso - Task connectedProductFlavorBuildTypeAndroidTest not found in root project

My build.gradle is like this:
productFlavors {
mainFlavor {
// ...
}
}
buildTypes {
debug {
// ...
}
buildType1 {// I write mock data classes for Espresso tests here
// ...
}
}
./gradlew tasks includes connectedMainFlavorDebugAndroidTest but not connectedMainFlavorBuildType1AndroidTest.
Why?
I want to specifically run Espresso tests for buildType1 only.
I'm not the owner of the project, I'm not allowed to use either mainFlavorDebug or someNewFlavorDebug to write Espresso tests
The answer from official documentation:
By default, all tests run against the debug build type. You can change
this to another build type by using the testBuildType property in your
module-level build.gradle file. For example, if you want to run your
tests against your "staging" build type, edit the file as shown in the
following snippet.
android {
...
testBuildType "staging" }

How does android.unitTests.returnDefaultValues work?

I know what having the following in an Android project's build.gradle is supposed to do. But how does it work? And what exactly are the default values returned? How do they compare with "real" values?
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
Per the documentation (emphasis added):
If the exceptions thrown by Android APIs in the android.jar are problematic for your tests, you can change the behavior so that methods instead return either null or zero by adding the following configuration in your project's top-level build.gradle file:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}
}
Caution: Setting the returnDefaultValues property to true should be done with care. The null/zero return values can introduce regressions in your tests, which are hard to debug and might allow failing tests to pass. Only use it as a last resort.

How can I ignore test failures with the gradle robolectric plugin?

I'm using the robolectric-gradle-plugin for robolectric unit tests. I don't want to fail a build on failed tests. Is there a way in DSL or a property not to fail a test on the build similar to -DtestFailureIgnore=true on the Surefire Maven plugin?
I've tried:
robolectric {
ignoreFailures = true
}
and
robolectric {
ignoreFailure = true
}
and -DignoreFailure=true on the command line.
I can't seem to find any documentation of how to do this, or any reference to ignoring tests in the source code.
answering very old question, so that it might help others who bump into here
testOptions {
unitTests.all {
setIgnoreFailures(true)
}
}
I would suggest not to continue building an APK if there are any failing tests. But if you want to build an APK without testing the only way right now is to use gradle build -x test[1]. This will run build and not run any tests.
[1]http://www.gradle.org/docs/current/userguide/userguide_single.html#sec:excluding_tasks_from_the_command_line
try without '='
robolectric {
ignoreFailures true
}

Categories

Resources