Skip to content

Commit

Permalink
DSL Polishing & Migration (#39)
Browse files Browse the repository at this point in the history
* Completed migration to new DSL location for junitPlatform & dependency handlers

Dependency Handlers:
* Replaced junit5() with junit5.unitTests()
* Replaced junit5Params() with junit5.parameterized()
* Replaced junit5EmbeddedRuntime() with junit5.unitTestsRuntime()
* Added junit5.instrumentationTests()

(Backwards-compatible versions of the old handlers are still available,
however they log a warning when used.)

Moved junitPlatform DSL from <root> to android.testOptions

(Backwards-compatible usage of the old DSL location is still allowed,
however that logs a warning when used.)

* Move Jacoco-related DSL to the new format

* Move instrumentationTest "enabled" property to function invocation as well

* Update renamed Gradle property

* Sample module still referred to old property

* Rename travis dir

* Fix testenv

* Upgraded README with the new info

* Small brush-ups

* Even more small brush-ups

* ...and a typo

* Release next version

fixes #37, #38
  • Loading branch information
mannodermaus authored Dec 3, 2017
1 parent c415fbc commit 6f1db59
Show file tree
Hide file tree
Showing 22 changed files with 793 additions and 273 deletions.
5 changes: 0 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ script: ./gradlew check --stacktrace
after_success:
- ./scripts/deploy_snapshot.sh

after_failure:
- cat $TRAVIS_BUILD_DIR/tests/testCommon/build/reports/tests/test/index.html
- cat $TRAVIS_BUILD_DIR/tests/testAgp2x/build/reports/tests/test/index.html
- cat $TRAVIS_BUILD_DIR/tests/testAgp3x/build/reports/tests/test/index.html

deploy:
provider: script
script: ./scripts/deploy_release.sh
Expand Down
155 changes: 99 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@

A Gradle plugin that allows for the execution of [JUnit 5][junit5gh] tests in Android environments.

## Why a separate plugin?

The JUnit Platform team provides a Gradle plugin for running JUnit 5 on the JVM. However,
this plugin is tailored to the needs of a "purely Java" application, and doesn't work in
the context of the multi-variant world that we live in on Android. Therefore, this plugin was born.

It configures a `junitPlatformTest` task for each registered build variant of a project.
Furthermore, it automatically attaches both the Jupiter & Vintage Engines
during the execution phase of your tests as well, so there's very little configuration
necessary to get your project up-and-running on the JUnit Platform.

Instructions on how to write JUnit 5 tests can be found [in their User Guide][junit5ug].
Furthermore, this repository provides a small showcase of the functionality provided by JUnit 5 [here][sampletests].

## Download

```groovy
buildscript {
dependencies {
classpath "de.mannodermaus.gradle.plugins:android-junit5:1.0.21"
classpath "de.mannodermaus.gradle.plugins:android-junit5:1.0.22"
}
}
```
Expand All @@ -23,32 +37,99 @@ apply plugin: "com.android.application"
apply plugin: "de.mannodermaus.android-junit5"
dependencies {
testImplementation junit5()
// (Required) Writing and executing Unit Tests on the JUnit Platform.
testImplementation junit5.unitTests()
// (Optional) If you need "parameterized tests"
testImplementation junit5Params()
// (Optional) If you need "Parameterized Tests".
testImplementation junit5.parameterized()
// (Optional) For running tests inside Android Studio 3.x (see below for details)
testCompileOnly junit5EmbeddedRuntime()
// (Optional) For running tests inside Android Studio 3.x
// Please refer to the "Android Studio Workarounds" section for more insight on this.
testCompileOnly junit5.unitTestsRuntime()
// (Optional) Writing and executing Instrumented Tests with the JUnit Platform Runner.
//
// IMPORTANT:
// By declaring this dependency, you have to use a minSdkVersion
// of at least 26, since the nature of JUnit 5 relies on APIs that aren't
// available on Android devices before then.
// Additionally, you are required to explicitly enable support for instrumented tests in the
// "junitPlatform" configuration closure (see the section below for details).
androidTestImplementation junit5.instrumentationTests()
}
```

## Usage
## Configuration

This plugin configures a `junitPlatformTest` task for each registered build variant of a project.
It automatically attaches both the Jupiter & Vintage Engines during the execution phase of your tests as well.
The plugin applies a configuration closure to your module's `android.testOptions`.
Inside it, you can use [all properties available through the default JUnit 5 Gradle plugin][junit5config].
However, there are a few more parameters that allow for more customization of the JUnit Platform
in your Android project. These are detailed below, alongside their default values:

More instructions on how to write JUnit 5 tests can be found [in their User Guide][junit5ug].
Furthermore, this repository provides a small showcase of the functionality provided by JUnit 5 [here][sampletests].
```groovy
android {
testOptions {
// Configuration closure added by the plugin;
// all configurable parameters related to JUnit 5 can be found here
junitPlatform {
// The JUnit Jupiter dependency version to use
jupiterVersion "5.0.2"
// The JUnit Vintage Engine dependency version to use
vintageVersion "4.12.2"
// Whether or not JUnit 5 test tasks should be affected by
// JVM Arguments, System Properties & Environment Variables
// declared through "unitTests.all" closures
applyDefaultTestOptions true
// Options related to running instrumented tests with JUnit 5.
// This is an incubating feature which utilizes the backwards-compatibility
// of the JUnit Platform in order to enhance the default Test Instrumentation Runner
// with new power. However, because of their experimental nature and steep minSdkVersion requirement,
// they are turned off by default. If you choose to enable them, you also have to declare
// the library dependency in your androidTest scope. Please refer to the "Setup"
// section for more details.
instrumentationTests {
enabled false
// The Android-Instrumentation-Test dependency version to use
version "0.1.0"
}
// Configuration of companion tasks for JaCoCo Reports,
// associated with each JUnit 5 task generated by the plugin.
// Just like the companion tasks themselves, these properties
// will only have an effect if your module declares the "jacoco" plugin as well.
// For each of the available report types, you can toggle the availability
// and destination folders that they write to.
jacoco {
xml {
enabled true
destination project.file()
}
html {
enabled true
destination project.file()
}
csv {
enabled true
destination project.file()
}
}
}
}
}
```

## Android Studio Workarounds

> **Note:**
>
>
> The following section deals with fixing Test Execution within **Android Studio 3**.
> Running your JUnit 5 tests directly from Android Studio 2.3.3 and earlier **will not work**:
> You will encounter an `AbstractMethodError` when trying to do so ([more information here][as2issue]).
>
>
> The cause of this error is similar in nature to the one described below, and related to outdated APIs.
> Unlike that issue though, we can't fix the `AbstractMethodError` inside IntelliJ's internal runtime
> in the same way. Therefore, please resort to using Gradle for unit testing in Android Studio 2.
Expand All @@ -73,52 +154,13 @@ To use this, add the following line alongside the other `junit5()` dependencies:

```groovy
dependencies {
testCompileOnly junit5EmbeddedRuntime()
}
```

## Extras

### Override Dependency Versions

Inside the configuration closure applied by the plugin, you can specify the same properties as you would
for a Java-based project with the JUnit Platform Gradle plugin.
However, there are some additional properties that you can apply:

```groovy
junitPlatform {
// The JUnit Jupiter dependency version to use; matches the platform's version by default
jupiterVersion "5.0.2"
// The JUnit Vintage Engine dependency version to use; matches the platform's version by default
vintageVersion "4.12.2"
}
```

### JaCoCo Integration

If the plugin detects the usage of [JaCoCo][jacoco] inside a project that it's being applied to,
it will automatically configure additional tasks to report the unit test coverage
of your application based on its JUnit 5 tests.
There is no additional setup required to enable this behaviour.
You can however customize the reports JaCoCo should generate.

Configuration is applied through the `jacoco` clause inside the plugin's DSL:

```groovy
apply plugin: "jacoco"
junitPlatform {
jacoco {
csvReport true
xmlReport true
htmlReport true
}
testCompileOnly junit5.unitTestsRuntime()
}
```

## Licenses

#### `android-junit5-embedded-runtime`
#### android-junit5-embedded-runtime:

```
Copyright 2000-2016 JetBrains s.r.o.
Expand All @@ -138,7 +180,7 @@ limitations under the License.

See also the [full License text](android-junit5-embedded-runtime/LICENSE).

#### Others:
#### Everything else:

```
Copyright 2017 Marcel Schnelle
Expand All @@ -161,7 +203,8 @@ See also the [full License text](LICENSE).


[junit5gh]: https://github.com/junit-team/junit5
[junit5ug]: http://junit.org/junit5/docs/current/user-guide
[junit5ug]: https://junit.org/junit5/docs/current/user-guide
[junit5config]: http://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle-junit-configure
[travisci]: https://travis-ci.org/mannodermaus/android-junit5
[as2issue]: https://github.com/mannodermaus/android-junit5/issues/19
[jacoco]: http://www.eclemma.org/jacoco
Expand Down
1 change: 1 addition & 0 deletions android-junit5-tests/common.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ tasks.withType(WriteClasspathResource).all {

test.testLogging {
events "passed", "skipped", "failed"
exceptionFormat = "full"
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport
import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest
import de.mannodermaus.gradle.plugins.junit5.util.TaskUtils
import org.gradle.api.Project
import org.gradle.api.ProjectConfigurationException

/*
* Unit testing the integration of JUnit 5
Expand Down Expand Up @@ -78,33 +79,28 @@ class AGP2PluginSpec extends BasePluginSpec {
expectedVariantTasks.each { assert runAllTask.getDependsOn().contains(it) }
}

def "Instrumentation Test Integration: Works with Product Flavors"() {
def "Instrumentation Test Integration: Attempting to use library without enabling throws Exception"() {
when:
Project project = factory.newProject(rootProject())
.asAndroidApplication()
.applyJunit5Plugin()
.build()

project.android {
productFlavors {
paid {
junit5InstrumentedTestsEnabled false
}
free {
junit5InstrumentedTestsEnabled true
}
testOptions.junitPlatform.instrumentationTests {
enabled = false
}
}

project.dependencies {
androidTestCompile junit5.instrumentationTests()
}

project.evaluate()

then:
def enabledFlavor = project.android.productFlavors.getByName("free")
def enabledArgs = enabledFlavor.getTestInstrumentationRunnerArguments()
assert enabledArgs.containsKey("runnerBuilder")

def disabledFlavor = project.android.productFlavors.getByName("paid")
def disabledArgs = disabledFlavor.getTestInstrumentationRunnerArguments()
assert !disabledArgs.containsKey("runnerBuilder")
def expect = thrown(ProjectConfigurationException)
expect.message.contains("The JUnit 5 Instrumentation Test library can only be used " +
"if support for them is explicitly enabled as well.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport
import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest
import de.mannodermaus.gradle.plugins.junit5.util.TaskUtils
import org.gradle.api.Project
import org.gradle.api.ProjectConfigurationException

/*
* Unit testing the integration of JUnit 5
Expand Down Expand Up @@ -144,38 +145,28 @@ class AGP3PluginSpec extends BasePluginSpec {
project.tasks.findByName("jacocoTestReportRelease") == null
}

def "Instrumentation Test Integration: Works with Product Flavors"() {
def "Instrumentation Test Integration: Attempting to use library without enabling throws Exception"() {
when:
Project project = factory.newProject(rootProject())
.asAndroidApplication()
.applyJunit5Plugin()
.build()

project.android {
// "All flavors must now belong to a named flavor dimension"
flavorDimensions "price"

productFlavors {
paid {
dimension "price"
junit5InstrumentedTestsEnabled false
}
free {
dimension "price"
junit5InstrumentedTestsEnabled true
}
testOptions.junitPlatform.instrumentationTests {
enabled = false
}
}

project.dependencies {
androidTestImplementation junit5.instrumentationTests()
}

project.evaluate()

then:
def enabledFlavor = project.android.productFlavors.getByName("free")
def enabledArgs = enabledFlavor.getTestInstrumentationRunnerArguments()
assert enabledArgs.containsKey("runnerBuilder")

def disabledFlavor = project.android.productFlavors.getByName("paid")
def disabledArgs = disabledFlavor.getTestInstrumentationRunnerArguments()
assert !disabledArgs.containsKey("runnerBuilder")
def expect = thrown(ProjectConfigurationException)
expect.message.contains("The JUnit 5 Instrumentation Test library can only be used " +
"if support for them is explicitly enabled as well.")
}
}
Loading

0 comments on commit 6f1db59

Please sign in to comment.