Skip to content

DSL Polishing & Migration #39

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

Merged
merged 12 commits into from
Dec 3, 2017
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