Step by step guide how to use Robolectric within Android Studio
Android Project
Create or open an existing android project with Android Studio. I used the "Blank Activity with Fragment" to proof my step by step guide and choosed Android SDK 21 as my target.
Latest tested build tools version:
classpath 'com.android.tools.build:gradle:1.1.3'
Speed up Unit Test compilation (Optional)
This part is optional but improve a bit the test compiling time. Replace the "Make" task with "Gradle-Aware-Task" for your run configuration (Run configuration -> Defaults -> JUnit -> Run External).
1. Start with a simple JUnit Test
Add JUnit test dependencies
testCompile 'junit:junit:4.12' testCompile "org.mockito:mockito-core:1.9.5"
And activate the experimental unit test feature: Settings / Gradle / Experimental
Now write a simple test and check if the unit test support works.
import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class SimpleUnitTest { @Test public void checkJUnitWork() { // failing test gives much better feedback // to show that all works correctly ;)
assertThat(true, is(false)); } }
Just right click on class or method and choose > Run and you will see a failing test, when you replace the expected value with true then the test should be successful.
2. Add initial Robolectric support
Add Robolectric as test dependency and sync your gradle configuration.
repositories { maven { url = "https://oss.sonatype.org/content/repositories/snapshots" } }
dependencies {
testCompile "org.robolectric:robolectric:3.0-SNAPSHOT"
}
Now you can use Robolectric to write tests. First we will only check if a Robolectric faked android context exists.
import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.RobolectricTestRunner; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(RobolectricTestRunner.class) public class RobolectricTest { @Test public void testIt() { // failing test gives much better feedback // to show that all works correctly ;)
assertThat(RuntimeEnvironment.application, nullValue()); } }
Just right click on class or method and choose > Run and you will see a failing test, when you replace the expected value with notNullValue() then the test should be successful.
3. Setup Robolectric Tests
If you now try to use a simple Robolectric example test you will only see failing tests and strange errors which give less feedback for the root cause https://github.com/robolectric/robolectric but here are the solutions.
Add TextView with id for testing
A fresh created android project contains most times a text view with "Hello World". Give this TextView an id to be accessible from code.
Test the TextView content
Let's start with basic test based on Robolectric which will first not work but then we fix all the errors step by step.
import android.app.Activity; import android.widget.TextView; import com.example.myapplication.MainActivity; import com.example.myapplication.R; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(CustomRobolectricRunner.class) public class RobolectricTest { @Test public void testIt() { Activity activity = Robolectric.setupActivity(MainActivity.class); TextView results = (TextView) activity.findViewById(R.id.textView); String resultsText = results.getText().toString(); // failing test gives much better feedback // to show that all works correctly ;) assertThat(resultsText, equalTo("Testing Android Rocks!")); } }
The CustomRobolectricRunner is optional when you prefer the @Config annotation. I prefer the custom runner because then there is less configuration at Android Studio necessary.
import org.junit.runners.model.InitializationError; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
public class CustomRobolectricRunner extends RobolectricTestRunner { public CustomRobolectricRunner(Class<?> testClass)
throws InitializationError { super(testClass); } }
Now start the test (which will fail) to get a feeling what we do at the following step.
4. Manifest location must be known
This is an important warning wich must be avoided:
WARNING: No manifest file found at ./AndroidManifest.xml.Falling back to the Android OS resources only.
With this guide, we will try to have minimal configuration effort and easy support for many setups. So here comes another solution where we make the AndroidManifest.XML location dynamic.
Remove the @Config Annotation and extend CustomRobolectricRunner constructor.
public CustomRobolectricRunner(Class<?> testClass) throws InitializationError { super(testClass); String buildVariant = (BuildConfig.FLAVOR.isEmpty() ? "" : BuildConfig.FLAVOR+ "/") + BuildConfig.BUILD_TYPE; String intermediatesPath = BuildConfig.class.getResource("") .toString().replace("file:", ""); intermediatesPath = intermediatesPath .substring(0, intermediatesPath.indexOf("/classes")); System.setProperty("android.package", BuildConfig.APPLICATION_ID); System.setProperty("android.manifest", intermediatesPath + "/manifests/full/" + buildVariant + "/AndroidManifest.xml"); System.setProperty("android.resources", intermediatesPath + "/res/" + buildVariant); System.setProperty("android.assets", intermediatesPath + "/assets/" + buildVariant); }
Expected result: You will not see the warning with Android Studio or command-line and a new message should appear likely following:
DEBUG: Loading resources for com.example.myapplication from ./app/src/main/res...
DEBUG: Loading resources for android from jar:/Users/UserName/.m2/repository/org/robolectric/android-all/4.3_r2-robolectric-0/android-all-4.3_r2-robolectric-0.jar!/res...
You are ready. More examples can be found at https://github.com/nenick/AndroidStudioAndRobolectric