Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Robolectric tests running in Android Studio but not on the command line

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

like image 658
Óscar López Avatar asked Mar 17 '15 17:03

Óscar López


People also ask

How do I run test cases on Android?

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 .

What is the significance of Robolectric?

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.

How Android Junit framework performs testing of an Android app?

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.

What should I test in unit tests Android?

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.


1 Answers

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
like image 100
Óscar López Avatar answered Sep 28 '22 07:09

Óscar López