I am a software engineer. You can view my portfolio at https://jaredsburrows.com/ and email me here: [email protected].
Gradle + Android Studio + Robolectric + Espresso + Mockito
License: Apache License 2.0
I am a software engineer. You can view my portfolio at https://jaredsburrows.com/ and email me here: [email protected].
Popular views such:
RecyclerView
RecyclerView.Adapter
optionsMenu
Hi,
I'm getting the following gradle error:
Execution failed for task ':test-lib:compileReleaseJavaWithJavac'.
javax/tools/DiagnosticListener : Unsupported major.minor version 52.0
I'm using java 7. It's set both in grade and in the project settings for Android Studio.
Do you have any ideas? version 52 is Java 8. Thanks
Hi,
Thanks for all the work you've put into this. It's highly appreciated.
I'm trying to run tests that use Robolectric and PowerMock. But I'm getting the following issue when using the "PowerMockRule" method.
Caused by: java.lang.IllegalStateException: PowerMockRule can only be used with the system classloader but was loaded by org.robolectric.internal.bytecode.InstrumentingClassLoader@2f21a0e5
Do you have any ideas? I've tried setting the suggestions by robolectric.
If I use @RunWith(RobolectricGradleTestRunner.class) I cannot test static methods, which is what I need.
Thanks!
Current setups:
Possible setups:
This code allows to embed own implementations of classes into mockable android JAR. In my case I embed: Log & SparseArray classes.
What script do:
patchMockableAndroidJar
task as finalizer of the mockableAndroidJar
copyMockableAndroidJar
, patchMockableAndroidJar
, copy${flavor}${buildType}TestResources
/* [ UNIT TESTING ] ================================================================================================= */
afterEvaluate {
/* Adjust Mockable Android task */
tasks.matching { it.name.startsWith('mockableAndroidJar') }.each { mock ->
// http://goo.gl/wXhBIF --> MockableJarGenerator.java
mock.finalizedBy patchMockableAndroidJar
}
/* Patch classpath of the test tasks for using our own version of the mockable JAR */
tasks.matching { it.name.startsWith("test") }.each { t ->
def old = "mockable-android-${androidTargetSdkVersion}.jar"
def mock = "${project.buildDir}\\intermediates\\patched-mockable-android-${androidTargetSdkVersion}.jar"
try {
t.classpath = files(t.classpath.files, mock).filter { it.name != old }
} catch (final Throwable ignored) {
// ignore task
}
}
/* Include resources. See: https://code.google.com/p/android/issues/detail?id=64887#c13 */
project.android.productFlavors.each { f ->
project.android.buildTypes.each { b ->
/** compose dynamic tasks */
def tName = "copy${f.name.capitalize()}${b.name.capitalize()}TestResources"
project.task(tName, type: Copy) {
description = "Make resources available for test: test${f.name.capitalize()}${b.name.capitalize()}."
group = 'Unit Testing'
from project.android.sourceSets["test"].resources.srcDirs
into "${project.buildDir}/intermediates/classes/test/${f.name}/${b.name}"
}
tasks.matching { it.name.equalsIgnoreCase("test${f.name}${b.name}") }.each {
it.dependsOn tName
}
}
}
}
/* Create a copy of original mockable Jar */
task copyMockableAndroidJar(type: Copy) {
description = 'Create backup copy of mockable-android-${api}.jar file'
group = 'Unit Testing'
// incremental build support
inputs.property "api", androidTargetSdkVersion
outputs.files "mockable-android-${androidTargetSdkVersion}-original.jar"
from "${project.buildDir}/intermediates/mockable-android-${androidTargetSdkVersion}.jar"
into "${project.buildDir}/intermediates/"
rename { String fileName ->
fileName.replace("mockable-android-${androidTargetSdkVersion}.jar",
"mockable-android-${androidTargetSdkVersion}-original.jar")
}
}
/* Exclude classes that we want to replace by own implementation */
task patchMockableAndroidJar(type: Zip, dependsOn: copyMockableAndroidJar) {
description = 'patch mockable-android-${api}.jar by own classes'
group = 'Unit Testing'
// set archive name and destination
entryCompression = ZipEntryCompression.STORED // speedup task by excluding compression
destinationDir = file("${project.buildDir}/intermediates")
archiveName = "patched-mockable-android-${androidTargetSdkVersion}.jar"
def source = "${project.buildDir}/intermediates/mockable-android-${androidTargetSdkVersion}-original.jar"
// exclude from Mocks Jar classes which we replace by own implementation
from(zipTree(source)) {
exclude '**/android/util/SparseArray.class'
exclude '**/android/util/Log.class'
}
}
I'm not 100% sure what's happening here. I get the following message when building from Gradle 2.10 without the wrapper. You can see the desired task was 'connectedDebugAndroidTest' and I wouldn't think that the 'test-lib' project should have anything that needs running in this case.
I found this in the unmodified project while trying to track down a similar error in a project that I modeled after the template.
UNEXPECTED TOP-LEVEL ERROR:
java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.regex.Pattern$BnM.optimize(Pattern.java:5408)
at java.util.regex.Pattern.compile(Pattern.java:1709)
at java.util.regex.Pattern.(Pattern.java:1351)
at java.util.regex.Pattern.compile(Pattern.java:1054)
at java.lang.String.replace(String.java:2226)
at com.android.dx.cf.direct.ClassPathOpener.compareClassNames(ClassPathOpener.java:197)
at com.android.dx.cf.direct.ClassPathOpener.access$000(ClassPathOpener.java:37)
at com.android.dx.cf.direct.ClassPathOpener$3.compare(ClassPathOpener.java:252)
at com.android.dx.cf.direct.ClassPathOpener$3.compare(ClassPathOpener.java:250)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:356)
at java.util.TimSort.sort(TimSort.java:230)
at java.util.Arrays.sort(Arrays.java:1435)
at java.util.Collections.sort(Collections.java:230)
at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:250)
at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
at com.android.dx.command.dexer.Main.processOne(Main.java:672)
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)
at com.android.dx.command.dexer.Main.run(Main.java:277)
at com.android.dx.command.dexer.Main.main(Main.java:245)
at com.android.dx.command.Main.main(Main.java:106)
:test-lib:transformClassesWithDexForDebugAndroidTest FAILED
FAILURE: Build failed with an exception.
Within the top level 'build.gradle' file, you have the following line:
.....
dependencies {
// testing
if (name.contains('test'))
.....
Is this supposed to match on ALL tests or just the unit test task?
Thanks, DD
Hi Jared,
I tried running test case to understand Espresso but came across below 2 issues , please review and clarify.
Issue 1:
./gradlew connectedAndroidTest
Got below error
AndroidGradleTemplate/app/all-libraries/src/androidTest/java/burrows/apps/example/template/instrumentation/MainActivityTest.java:20: warning: [deprecation] ActivityInstrumentationTestCase2(String,Class) in ActivityInstrumentationTestCase2 has been deprecated
super("burrows.apps.example.template", MainActivity.class);
^
where T is a type-variable:
T extends Activity declared in class ActivityInstrumentationTestCase2
error: warnings found and -Werror specified
1 error
1 warning
:app:all-libraries:compileDebugAndroidTestJavaWithJavac FAILED
Issue 2:
Right click an instrumentation test located in src/main/androidTest and click test
This path is wrong , README needs updation
Thanks & Regards,
Vikram
Any non trivial project is going to have more than one module.
Illegal character in opaque part at index 11: jar:file:G:\FileServer\OpenAndroid\teck\UnitTest\JaCoCofull\android-gradle-master\build\intermediates\apk_for_local_test\debugUnitTest\packageDebugUnitTestForUnitTest\apk-for-local-test.ap_
Possible add a RecyclerView
test.
Lint + TestResults + Jacoco
What script do:
Reporting
and Reporting routine
checkOPenResults
, lintOpenResults
, testOpenResults
, jacocoOpenResults
Script expect several things:
open-test-results.sh
// tools: Code Analysis Tools
import org.apache.tools.ant.taskdefs.condition.Os
buildscript {
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
dependencies {
// https://github.com/aaschmid/gradle-cpd-plugin
classpath 'de.aaschmid.gradle.plugins:gradle-cpd-plugin:+'
}
}
task checkOpenResults(dependsOn: ['check']) {
description = 'Open reports of ALL attached Quality Tools in Web browser'
group = 'Reporting'
// do nothing, it used for dependency graph building only
}
if (ENABLE_QUALITY) {
/* [ LINT ] ===================================================================================================== */
task lintOpenResults(dependsOn: ['lint']) {
description = 'Open all reports for all flavors and build configurations.'
group = 'Reporting'
}
checkOpenResults.dependsOn lintOpenResults
afterEvaluate { iterateVariants(this.&createLintOpenResultsTask) }
}
/* [ UNIT Test Results ] ======================================================================================== */
if (ENABLE_QUALITY && useTesting) {
task testOpenResults(dependsOn: ['test']) {
description = 'Open all Unit Tests results for all flavors and build types.'
group = 'Reporting'
}
checkOpenResults.dependsOn testOpenResults
afterEvaluate { iterateVariants(this.&createTestOpenResultsTask) }
}
/* [ JACOCO coverage ] ========================================================================================== */
if (ENABLE_QUALITY && useTesting && useJacoco) {
// Helpers:
// http://forums.gradle.org/gradle/topics/gradle_1_6_jacoco_in_multi_module_build
// http://goo.gl/1ZrSl0 --> JacocoPlugin.groovy
// http://goo.gl/0pkjse --> JacocoReportTask.groovy
task jacocoOpenResults(dependsOn: ['test']) {
// Task opens report file in default web-browser
description = 'open Code Coverage for Unit Tests in web browser'
group = 'Reporting'
}
checkOpenResults.dependsOn jacocoOpenResults
afterEvaluate { iterateVariants(this.&createJacocoOpenResultsTask) }
}
/* [ HELPERS ] ====================================================================================================== */
def adoptToOs(String path) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) { // windows
path = path.replace("/", "\\")
} else if (Os.isFamily(Os.FAMILY_UNIX)) { // linux
path = path.replace("\\", "/")
} else if (Os.isFamily(Os.FAMILY_MAC)) { // mac os
path = path.replace("\\", "/")
}
return path;
}
def openExternalBrowser(String path) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) { // windows
return ['cmd.exe', '/C', path]
} else if (Os.isFamily(Os.FAMILY_UNIX)) { // linux
return ["${rootProject.rootDir}/gradle/open-test-results.sh", path]
} else if (Os.isFamily(Os.FAMILY_MAC)) { // mac os
// TODO: implement me
return ['']
}
}
def iterateVariants(func) {
if (project.android.productFlavors.size() > 0) {
project.android.productFlavors.each { f ->
iterateBuildTypes(f.name, func)
}
} else {
iterateBuildTypes('', func)
}
}
def iterateBuildTypes(String flavor, func) {
project.android.buildTypes.each { b ->
func(flavor, b.name);
}
}
/* [ TASKS CREATORS ] =============================================================================================== */
def createLintOpenResultsTask(String f, String b) {
def flavor = f.capitalize()
def buildType = b.capitalize()
def taskName = "openLintReport${flavor}${buildType}"
project.task(taskName, type: Exec, dependsOn: ["lint${flavor}${buildType}"]) {
description = "open lint${flavor}${buildType} results in current web browser."
group = 'Reporting Routine'
ignoreExitValue true // task is not critical, ignore it failure
// path is relative to current project path: {project}\\samples\\sample-01
// file:/C:/Android/_projects_/meter/samples/sample-01/build/outputs/lint-results.html
def lintFile = "lint-results-${flavor}${buildType}-fatal.html"
def path = """${project.buildDir}\\outputs\\${lintFile}"""
project.logger.info(" report: ${adoptToOs(path)}")
commandLine openExternalBrowser(adoptToOs(path))
}
lintOpenResults.dependsOn taskName
}
def createTestOpenResultsTask(String f, String b) {
def flavor = f.capitalize()
def buildType = b.capitalize()
def taskName = "openTestReport${flavor}${buildType}"
project.task(taskName, type: Exec, dependsOn: ["test${flavor}${buildType}"]) {
// Task opens report file in default web-browser
description = 'open Unit Tests results in current web browser'
group = 'Reporting Routine'
ignoreExitValue true // task is not critical, ignore it failure
// path is relative to current project path: {project}\\samples\\sample-01
// file:///C:/Android/_projects_/meter/library/build/reports/tests/index.html
def path = """${project.buildDir}\\reports\\tests\\${flavor}${buildType}\\index.html"""
project.logger.info(" report: ${adoptToOs(path)}")
commandLine openExternalBrowser(adoptToOs(path))
}
testOpenResults.dependsOn taskName
}
def createJacocoOpenResultsTask(String f, String b) {
def flavor = f.capitalize()
def buildType = b.capitalize()
def taskName = "generateJacocoReport${flavor}${buildType}"
project.task(taskName, type: JacocoReport, dependsOn: ["test${flavor}${buildType}"]) {
description = 'Generates Jacoco coverage reports: XML and HTML'
group = 'Reporting Routine'
// use hidden configuration, for details look into JacocoPlugin.groovy
jacocoClasspath = project.configurations['androidJacocoAnt']
// exclude auto-generated classes and tests
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/Mock*.*']
def testsTree = fileTree(dir: "${project.buildDir}/intermediates/classes/test/${flavor}/${buildType}", excludes: fileFilter)
def flavorTree = fileTree(dir: "${project.buildDir}/intermediates/classes/${flavor}/${buildType}", excludes: fileFilter)
// sources
def testsSrc = "${project.projectDir}/src/test/java"
def testsFlavorSrc = "${project.projectDir}/src/test${flavor}/java"
def testsBuildSrc = "${project.projectDir}/src/test${buildType}/java"
def testsVariantSrc = "${project.projectDir}/src/test${flavor}${buildType}/java"
def mainSrc = "${project.projectDir}/src/main/java"
// TODO: confirm order of the src dirs
sourceDirectories = files([testsSrc, testsFlavorSrc, testsBuildSrc, testsVariantSrc, mainSrc])
classDirectories = files([testsTree, flavorTree])
executionData = fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec'])
reports {
xml {
enabled = true
destination = "${project.buildDir}/reports/jacoco/${flavor}/${buildType}/jacoco.xml"
}
csv.enabled false
html {
enabled = true
destination = "${project.buildDir}/reports/jacoco/${flavor}/${buildType}"
}
}
}
def taskName2 = "openJacocoReport${flavor}${buildType}"
project.task(taskName2, type: Exec, dependsOn: [taskName]) {
// Task opens report file in default web-browser
description = 'open Code Coverage for Unit Tests in web browser'
group = 'Reporting Routine'
ignoreExitValue true // task is not critical, ignore it failure
def path = """${project.buildDir}/reports/jacoco/${flavor}/${buildType}/index.html"""
rootProject.logger.info(' report: ' + path)
commandLine openExternalBrowser(adoptToOs(path))
}
jacocoOpenResults.dependsOn taskName2
}
#!/bin/bash
## http://www.gnu.org/software/bash/manual/bashref.html
## http://stackoverflow.com/questions/3124556/clean-way-to-launch-the-web-browser-from-shell-script
## http://askubuntu.com/questions/8252/how-to-launch-default-web-browser-from-the-terminal
executor=gnome-open
type gnome-open >/dev/null 2>&1 || {
echo >&2 "gnome-open not installed.";
executor=sensible-browser
type sensible-browser >/dev/null 2>&1 || {
echo >&2 "sensible-browser not installed."
executor=x-www-browser
type x-www-browser >/dev/null 2>&1 || {
echo >&2 "x-www-browser not installed.";
executor=xdg-open
type xdg-open >/dev/null 2>&1 || {
echo >&2 "xdg-open not installed. Aborting.";
exit 1;
}
}
}
}
URL=$1
echo "Found executor: $executor"
# run in background
exec "$executor" "$URL" &
Add mavenCentral()
to allprojects
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.