I'm trying to run unit tests using Robolectric; they run fine under Android Studio, but the exact same tests fail when running in the command line - which is a big deal, I need to be able to run them from my continuous integration platform, not just from an IDE.
I suspect that I'm missing some command-line argument (say, a classpath or something similar) or calling the wrong task - otherwise the test wouldn't run at all from Android Studio. Some relevant details; the test looks like this:
@RunWith(RobolectricTestRunner.class)
@Config(manifest = "app/src/main/AndroidManifest.xml", resourceDir = "res", emulateSdk = 19)
public class SplashActivityTest {
@Test
public void testActivity() {
SplashActivity splashActivity = new SplashActivity();
String appName = splashActivity.getString(R.string.app_name); // HERE, line 20
assertEquals(appName, "App");
}
}
As mentioned above, it runs fine in Android Studio (by right-clicking the test file and selecting Run 'SplashActivityTest'
) but when running it from the command line it fails in the line marked with HERE
, with the following stack trace:
android.content.res.Resources$NotFoundException: unknown resource 2131492893
at org.robolectric.shadows.ShadowAssetManager.getAndResolve(ShadowAssetManager.java:309)
at org.robolectric.shadows.ShadowAssetManager.getResourceText(ShadowAssetManager.java:69)
at android.content.res.AssetManager.getResourceText(AssetManager.java)
at android.content.res.Resources.getText(Resources.java:240)
at org.robolectric.shadows.ShadowResources.getText(ShadowResources.java:361)
at android.content.res.Resources.getText(Resources.java)
at android.content.res.Resources.getString(Resources.java:330)
at org.robolectric.shadows.ShadowContext.getString(ShadowContext.java:39)
at org.robolectric.shadows.ShadowContextWrapper.getString(ShadowContextWrapper.java:69)
at android.content.Context.getString(Context.java)
at path.to.myApp.activities.SplashActivityTest.testActivity(SplashActivityTest.java:20)
// ... and so on ...
I'm using this to run from the command line (notice that in here and in Android Studio I'm using the Gradle wrapper):
project-root$ ./gradlew test --continue
Also: I'm using Android Studio 1.1.0
, Gradle version is 2.3
, Robolectric's version is 3.0-SNAPSHOT
and Robolectric's Gradle plugin version is 1.0.1
To run all tests in a class or a specific method, open the test file in the Code Editor and do either of the following: Press the Run test icon in the gutter. Right-click on the test class or method and click Run . Select the test class or method and use shortcut Control+Shift+R .
Overview to Robolectric. It allows Android applications to be tested on the JVM without an emulator or device. Running Android tests on the JVM usually fails because the Android core libraries included with the SDK, specifically the android. jar file, only contain stub implementations of the Android classes.
Unit testing is done to ensure that developers write high-quality and errorless code. It is advised to write Unit tests before writing the actual app, you will write tests beforehand and the actual code will have to adhere to the design guidelines laid out by the test.
Unit tests or small tests only verify a very small portion of the app, such as a method or class. End-to-end tests or big tests verify larger parts of the app at the same time, such as a whole screen or user flow. Medium tests are in between and check the integration between two or more units.
It turns out that this is a known issue. The resources of a project are looked for in the wrong directory when running tests from the command line, here's a link to the issue; for now the workaround is to write a custom test runner as demonstrated here:
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
public RobolectricGradleTestRunner(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);
}
}
The test cases must be updated accordingly to use the custom test runner instead of the @Config
annotation:
@RunWith(RobolectricGradleTestRunner.class)
public class SplashActivityTest {
// tests same as before
}
Also, it's recommended to perform a clean
when running the tests from the command line, given that the workaround depends on the contents of the build directory, we don't want to have stale data there:
project-root$ ./gradlew clean test --continue
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With