Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic Android instrumented and unit tests (closes #2341) #2360

Merged
merged 6 commits into from Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
@@ -1,6 +1,7 @@
buildscript {
repositories {
mavenLocal() //for local testing of mockito-release-tools
google()
maven { url 'https://plugins.gradle.org/m2/' }
}

Expand All @@ -13,6 +14,8 @@ buildscript {
classpath 'org.shipkit:shipkit-auto-version:1.1.19'

classpath 'com.google.googlejavaformat:google-java-format:1.10.0'
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
}
}

Expand Down Expand Up @@ -47,6 +50,7 @@ apply from: 'gradle/dependencies.gradle'
allprojects { proj ->
repositories {
mavenCentral()
google()
}
plugins.withId('java') {
proj.apply from: "$rootDir/gradle/errorprone.gradle"
Expand Down
7 changes: 7 additions & 0 deletions gradle.properties
@@ -1,3 +1,10 @@
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
8 changes: 8 additions & 0 deletions gradle/dependencies.gradle
Expand Up @@ -40,4 +40,12 @@ libraries.kotlin = [

stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}",
coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt',
gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}",
]
libraries.android = [
ktx: 'androidx.core:core-ktx:1.5.0',
compat: 'androidx.appcompat:appcompat:1.3.0',
material: 'com.google.android.material:material:1.3.0',
junit: 'androidx.test.ext:junit:1.1.2',
espresso: 'androidx.test.espresso:espresso-core:3.3.0',
]
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=e58cdff0cee6d9b422dcd08ebeb3177bc44eaa09bd9a2e838ff74c408fe1cbcd
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip
distributionSha256Sum=3239b5ed86c3838a37d983ac100573f64c1f3fd8e1eb6c89fa5f9529b5ec091d
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
37 changes: 17 additions & 20 deletions gradlew
Expand Up @@ -7,7 +7,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -82,6 +82,7 @@ esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
Expand Down Expand Up @@ -125,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
Expand All @@ -154,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

Expand All @@ -175,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"
6 changes: 5 additions & 1 deletion gradlew.bat
Expand Up @@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

Expand Down Expand Up @@ -81,6 +84,7 @@ set CMD_LINE_ARGS=%*

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Expand Up @@ -9,6 +9,7 @@ include("deprecatedPluginsTest",
"kotlinTest",
"kotlinReleaseCoroutinesTest",
"android",
"androidTest",
"junit-jupiter",
"junitJupiterExtensionTest",
"junitJupiterInlineMockMakerExtensionTest",
Expand Down
4 changes: 4 additions & 0 deletions subprojects/androidTest/.gitignore
@@ -0,0 +1,4 @@
/captures
.externalNativeBuild
.cxx
local.properties
30 changes: 30 additions & 0 deletions subprojects/androidTest/README.md
@@ -0,0 +1,30 @@
# Android Testing Project
## Overview
Unlike the other subprojects, this project is designed to run against the Android toolchain rather than standard Kotlin/Java. This means it should be opened in Android Studio, rather than IntelliJ.

This project was introduced to provide a basic level of assurance for Mockito's correct operation on the Android platform.

This project features the following:
* the minimum app project prerequisites - it is not intended to be run as an actual app
* basic JUnit tests
* basic instrumented tests

While JUnit tests run on the local machine, instrumented tests in the Android context run on a device, which can be an emulator. Due to the Android runtime design, the implementation options for instrumented tests are constrained compared to normal JUnit tests; for example, it is not possible to mock final classes.

## Library Version Definitions / ByteBuddy

Libraries and their versions are defined in `ext.library-versions.gradle`. It is here that you can specify which version of Mockito to test.

ByteBuddy is an optional declaration. If you specify '0' for the ByteBuddy version, e.g.

`bytebuddy_version = '0'`

then ByteBuddy will be as declared by Mockito's own dependencies.

However you are able to specify a _newer_ version if you would like to test an update, and you can do this by specifying a real version, e.g.:

`bytebuddy_version = '1.11.7'`

This will cause ByteBuddy to be directly included in this project using the specified version.

This override is subject to Gradle's dependency resolution process where it reconciles different versions - Mockito's library inclusion vs. the project one. This usually means the newest version is accepted; therefore if you specify a version of ByteBuddy that's _older_ than Mockito's one, it's unlikely to apply it. You would need to use Gradle's `resolutionStrategy` definition to force this.
53 changes: 53 additions & 0 deletions subprojects/androidTest/androidTest.gradle
@@ -0,0 +1,53 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
compileSdkVersion 30
buildToolsVersion "30.0.3"

defaultConfig {
applicationId "org.mockitousage.androidtest"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

apply from: "$rootDir/gradle/dependencies.gradle"

dependencies {
implementation libraries.kotlin.stdlib
implementation libraries.android.ktx
implementation libraries.android.compat
implementation libraries.android.material

testImplementation project(":inline")
testImplementation libraries.junit4
testImplementation libraries.junitJupiterApi
testImplementation libraries.junitJupiterEngine

androidTestImplementation libraries.android.junit
androidTestImplementation libraries.android.espresso
androidTestImplementation project(":android")
}
21 changes: 21 additions & 0 deletions subprojects/androidTest/proguard-rules.pro
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,78 @@
package org.mockitousage.androidtest

import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify

@RunWith(AndroidJUnit4::class)
class BasicInstrumentedTests {

private var closeable: AutoCloseable? = null

@Mock private lateinit var mockedViaAnnotationBasicOpenClass: BasicOpenClass
@Mock private lateinit var mockedViaAnnotationBasicInterface: BasicInterface

@Before
fun setup() {
closeable = MockitoAnnotations.openMocks(this)
}

@After
@Throws(Exception::class)
fun releaseMocks() {
closeable?.close()
}

//Open class

@Test
fun mockAndUseBasicClassUsingAnnotatedMock() {
val basicClass = BasicOpenClassReceiver(mockedViaAnnotationBasicOpenClass)
basicClass.callDependencyMethod()
throw Exception("java")
}

@Test
fun mockAndUseBasicClassUsingLocalMock() {
val basicOpenClass = mock(BasicOpenClass::class.java)
val basicReceiver = BasicOpenClassReceiver(basicOpenClass)
basicReceiver.callDependencyMethod()
}

@Test
fun mockAndUseBasicClassWithVerify() {
val basicClass = BasicOpenClassReceiver(mockedViaAnnotationBasicOpenClass)
basicClass.callDependencyMethod()
verify(mockedViaAnnotationBasicOpenClass).emptyMethod()
}

//Interface

@Test
fun mockAndUseBasicInterfaceUsingAnnotatedMock() {
val receiver = BasicInterfaceReceiver(mockedViaAnnotationBasicInterface)
receiver.callInterfaceMethod()
verify(mockedViaAnnotationBasicInterface).interfaceMethod()
}

@Test
fun mockAndUseBasicInterfaceUsingLocalMock() {
val basicInterface = mock(BasicInterface::class.java)
val receiver = BasicInterfaceReceiver(basicInterface)
receiver.callInterfaceMethod()
}

@Test
fun mockAndUseBasicInterfaceAndVerify() {
val basicInterface = mock(BasicInterface::class.java)
val receiver = BasicInterfaceReceiver(basicInterface)
receiver.callInterfaceMethod()
verify(basicInterface).interfaceMethod()
}
}
13 changes: 13 additions & 0 deletions subprojects/androidTest/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mockitousage.androidtest">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MockitoAndroidTest" />

</manifest>