80

I am using Android Studio/IntelliJ to build on an existing Android project and would like to add some simple JUnit unit tests. What is the right folder to add such tests on?

The android Gradle plug-in defines a directory structure with src/main/java for the main source code and src/instrumentTest/java for Android tests.

Trying to add my JUnit tests in instrumentTest didn't work for me. I can run it as an Android test (that's what that directory seems for) but that's not what I'm looking for - I just want to run a simple JUnit test. I tried creating a JUnit run configuration for this Class but that also didn't work - I'm supposing because I'm using a directory that is flagged as Android Test instead of Source.

If I create a new source folder and marki it as such in Project Structure, this will get wiped next time IntelliJ refreshes the project configuration from the gradle build files.

What is the more appropriate way of configuring JUnit tests in an gradle-based android project on IntelliJ? Which directory structure to use for this?

hoss
  • 2,430
  • 1
  • 27
  • 42
Julian Cerruti
  • 1,918
  • 2
  • 16
  • 16
  • Unfortunately I can't help you with the tests as I'm still struggling with this myself, but you can use this to stop IntelliJ wiping the source folders: `java.srcDirs = ['src/main/java','src/instrumentTest/java']` in the `sourceSets` section of the `build.gradle` – Rob Holmes Jul 31 '13 at 20:47
  • Its not fun to set up, nor simple to maintain, but I somehow got it running using the default `/src/androidTest/java` folder with robolectric JUnit tests, see: http://stackoverflow.com/a/25473702/1406325 – Flo Aug 24 '14 at 16:24
  • Android Studio now supports Local Unit Testing with JUnit 4: http://stackoverflow.com/a/30273301/885464 – Lorenzo Polidori May 16 '15 at 08:23
  • @Julian Cerruti Update:Android Studio Demo for how to run testcase http://goo.gl/ac06C0 and example to run network call test http://goo.gl/bQFlmU – Nitesh Tiwari Jul 30 '15 at 08:38
  • The directories structures for tests and tested classes must fit. Here is how to do it easily: stackoverflow.com/a/36057080/715269 – Gangnus Mar 18 '16 at 08:11

6 Answers6

36

Normally, you can't. Welcome to the world of Android, where all tests must run on a device(except Robolectric).

The main reason is that you don't actually have the framework's sources - even if you convince the IDE to run the test locally, you will immediately get a "Stub! Not implemented" exception. "Why?" you might wonder? Because the android.jar that the SDK gives you is actually all stubbed out - all the classes and methods are there but they all just throw an exception. It's there to provide an API but not there to give you any actual implementation.

There's a wonderful project called Robolectric which implements a lot of the framework just so you can run meaningful tests. Coupled with a good mock framework (e.g., Mockito), it makes your job manageable.

Gradle plugin: https://github.com/robolectric/robolectric-gradle-plugin

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Delyan
  • 8,881
  • 4
  • 37
  • 42
  • 1
    Thank you, Deylan. I am after simple tests that do not use any Android infrastructure, so either creating a new project like you suggest or tweaking the Gradle Android configuration to accommodate regular tests will be the route to go. Unfortunately I still wasn't able to get it to work - it keeps failing for one reason or another - but will post the result if I ever get it to work – Julian Cerruti Jul 25 '13 at 16:54
  • 1
    In that case, you want to make a new plain java Gradle project (i.e. `apply plugin: 'java'`), add it to 'settings.gradle', have it as a dependency to your Android project (`compile project(':plainJavaProjectName')`), and just run the `test` task. (Not tested but should definitely work!) – Delyan Jul 25 '13 at 16:57
  • 3
    Tests in a plain java gradle project work fine with command line, but not inside Android Studio where I'm getting a "class not found MyTestClass" error. That's strange because AS and the command line should use gradle the same way. – lukasz Sep 01 '13 at 16:56
  • lukas: did you find a fix for that? I'm stuck at the same situation. Class not found running for AS. – Kenneth Nov 14 '13 at 06:39
  • 1
    Avoid gradle if you want to use JUnit testing, maven is a better option. – Jeroen Mar 12 '14 at 23:56
  • 4
    Can you justify that statement? Why is Maven better? – RichieHH Mar 30 '14 at 20:44
  • Android Studio is catching up slowly, see the recent beta: http://tools.android.com/tech-docs/unit-testing-support – racs Feb 08 '15 at 20:36
  • Android Studio now supports Local Unit Testing with JUnit 4: http://stackoverflow.com/a/30273301/885464 – Lorenzo Polidori May 16 '15 at 08:13
32

Intro

Note that at the time of writing robolectric 2.4 is the latest version and has no support for appcompat v7 library. Support wil be added in robolectric 3.0 release (no ETA yet). Also ActionBar Sherlock can cause issues with robolectric.

To use Robolectric within Android Studio you have 2 options:

(Option 1) - Running JUnit tests with Android Studio using a Java module

This technique uses a java module for all your tests with a dependency to your android module and a custom test runner with some magic:

Instructions can be found here: http://blog.blundellapps.com/how-to-run-robolectric-junit-tests-in-android-studio/

Also check the link at the end of that post for running the tests from android studio.

(Option 2) - Running JUnit Tests with Android Studio using robolectric-gradle-plugin

I encountered a few issues setting up junit tests to run from gradle in Android Studio.

This is a very basic sample project for running junit tests from a gradle based project in Android Studio: https://github.com/hanscappelle/android-studio-junit-robolectric This was tested with Android Studio 0.8.14, JUnit 4.10, robolectric gradle plugin 0.13+ and robolectric 2.3

Buildscript (project/build.gradle)

The build script is the build.gradle file in the root of you project. There I had to add robolectric gradle plugin to classpath

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.13.2'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'org.robolectric:robolectric-gradle-plugin:0.13.+'

    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Project buildscript (App/build.gradle)

In the build script of your app module use the robolectric plugin, add robolectric config and add androidTestCompile dependencies.

apply plugin: 'com.android.application'
apply plugin: 'robolectric'

android {
    // like any other project
}

robolectric {
    // configure the set of classes for JUnit tests
    include '**/*Test.class'
    exclude '**/espresso/**/*.class'

    // configure max heap size of the test JVM
    maxHeapSize = '2048m'

    // configure the test JVM arguments
    jvmArgs '-XX:MaxPermSize=512m', '-XX:-UseSplitVerifier'

    // configure whether failing tests should fail the build
    ignoreFailures true

    // use afterTest to listen to the test execution results
    afterTest { descriptor, result ->
        println "Executing test for {$descriptor.name} with result: ${result.resultType}"
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    androidTestCompile 'org.robolectric:robolectric:2.3'
    androidTestCompile 'junit:junit:4.10'
}

Create JUnit Test Classes

Now put the test classes in the default location (or update gradle config)

app/src/androidTest/java

And name your tests classes ending with Test (or again update config), extending junit.framework.TestCase and annotate test methods with @Test.

package be.hcpl.android.mytestedapplication;

import junit.framework.TestCase;
import org.junit.Test;

public class MainActivityTest extends TestCase {

    @Test
    public void testThatSucceeds(){
        // all OK
        assert true;
    }

    @Test
    public void testThatFails(){
        // all NOK
        assert false;
    }
}

Execute Tests

Next execute the tests using gradlew from command line (make it executable using chmod +x if needed)

./gradlew clean test

Sample output:

Executing test for {testThatSucceeds} with result: SUCCESS
Executing test for {testThatFails} with result: FAILURE

android.hcpl.be.mytestedapplication.MainActivityTest > testThatFails FAILED
    java.lang.AssertionError at MainActivityTest.java:21

2 tests completed, 1 failed                                  
There were failing tests. See the report at: file:///Users/hcpl/Development/git/MyTestedApplication/app/build/test-report/debug/index.html
:app:test                      

BUILD SUCCESSFUL

Troubleshooting

alternative source directories

Just like you can have your java source files somewhere else you can move your test source files. Just update gradle sourceSets config.

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        androidTest {
            setRoot('tests')
        }
    }

package org.junit does not exist

You forgot to add the junit test dependency in the app build script

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    androidTestCompile 'org.robolectric:robolectric:2.3'
    androidTestCompile 'junit:junit:4.10'
}

java.lang.RuntimeException: Stub!

You're running this test with the run configurations from Android Studio instead of command line (Terminal tab in Android Studio). To run it from Android Studio you'll have to update the app.iml file to have the jdk entry listed at the bottom. See deckard-gradle example for details.

Complete error example:

!!! JUnit version 3.8 or later expected:

java.lang.RuntimeException: Stub!
    at junit.runner.BaseTestRunner.<init>(BaseTestRunner.java:5)
    at junit.textui.TestRunner.<init>(TestRunner.java:54)
    at junit.textui.TestRunner.<init>(TestRunner.java:48)
    at junit.textui.TestRunner.<init>(TestRunner.java:41)
    at com.intellij.rt.execution.junit.JUnitStarter.junitVersionChecks(JUnitStarter.java:190)
    at com.intellij.rt.execution.junit.JUnitStarter.canWorkWithJUnitVersion(JUnitStarter.java:173)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:56)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

ERROR: JAVA_HOME is set to an invalid directory

See this SO question for a solution. Add the below export to you bash profile:

export JAVA_HOME=`/usr/libexec/java_home -v 1.7`  

The complete error log:

ERROR: JAVA_HOME is set to an invalid directory: export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.

Test Class not found

If you want to run your tests from Android Studio Junit Test runner instead you'll have to expand the build.gradle file a little more so that android studio can find your compiled test classes:

sourceSets {
    testLocal {
        java.srcDir file('src/test/java')
        resources.srcDir file('src/test/resources')
    }
}

android {

    // tell Android studio that the instrumentTest source set is located in the unit test source set
    sourceSets {
        instrumentTest.setRoot('src/test')
    }
}

dependencies {

    // Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well
    testLocalCompile 'junit:junit:4.11'
    testLocalCompile 'com.google.android:android:4.1.1.4'
    testLocalCompile 'org.robolectric:robolectric:2.3'

    // Android Studio doesn't recognize the `testLocal` task, so we define the same dependencies as above for instrumentTest
    // which is Android Studio's test task
    androidTestCompile 'junit:junit:4.11'
    androidTestCompile 'com.google.android:android:4.1.1.4'
    androidTestCompile 'org.robolectric:robolectric:2.3'

}

task localTest(type: Test, dependsOn: assemble) {
    testClassesDir = sourceSets.testLocal.output.classesDir

    android.sourceSets.main.java.srcDirs.each { dir ->
        def buildDir = dir.getAbsolutePath().split('/')
        buildDir =  (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')

        sourceSets.testLocal.compileClasspath += files(buildDir)
        sourceSets.testLocal.runtimeClasspath += files(buildDir)
    }

    classpath = sourceSets.testLocal.runtimeClasspath
}

check.dependsOn localTest

from: http://kostyay.name/android-studio-robolectric-gradle-getting-work/

Some more resources

The best articles I found around this are:

Pang
  • 9,564
  • 146
  • 81
  • 122
hcpl
  • 17,382
  • 7
  • 72
  • 73
  • URLs for the first 2 links seem to have changed. Here are working ones: [Option 1](https://blog.blundellapps.co.uk/how-to-run-robolectric-junit-tests-in-android-studio/), [Option 2](https://blog.blundellapps.co.uk/android-gradle-app-with-robolectric-junit-tests/) – WebViewer Jan 08 '23 at 19:21
23

As of Android Studio 1.1, the answer is now simple: http://tools.android.com/tech-docs/unit-testing-support

Erik B
  • 2,810
  • 1
  • 34
  • 38
  • 4
    Does this mean that Robolectric is no longer useful? Will this essentially do the same thing? – Andrea Thacker Apr 14 '15 at 20:23
  • 1
    Note: As of Android Studio 2.0 Beta5 the in the link describe "Test Artifact" selector is missing and both modes (junit & insttrumentation) are enabled simultaneously by default. See here for more info http://stackoverflow.com/questions/35708263/test-artifact-selector-missing-gone-from-build-variants-in-android-studio-2-beta – Patrick Apr 18 '16 at 12:24
  • Please stop posting links as an answer. The link is dead. – Neon Warge Feb 21 '22 at 13:09
3

This is now supported in Android Studio starting with Android Gradle plugin 1.1.0, check this out:

https://developer.android.com/training/testing/unit-testing/local-unit-tests.html

Sample app with local unit tests on GitHub:

https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicSample

Lorenzo Polidori
  • 10,332
  • 10
  • 51
  • 60
1

For Android Studio 1.2+ setting up a project for JUnit is pretty simple try to follow along this tutorial:

This is the simplest part setting up a project for JUnit:

https://io2015codelabs.appspot.com/codelabs/android-studio-testing#1

Follow along the past link until "Running your tests"

Now if you want to integrate with intrumentation test follow along from here:

https://io2015codelabs.appspot.com/codelabs/android-studio-testing#6

CommonSenseCode
  • 23,522
  • 33
  • 131
  • 186
0

Please see this tutorial from the Android Developers official site. This article also shows how to create mock-ups for your testing.

By the way, you should note that the scope of the dependencies for simple JUnit test should be "testCompile".

BlueMice
  • 333
  • 4
  • 15