Robolectric: Resources$NotFoundException: String resource ID with Android Gradle Plugin 3 - android

Android Studio 3.0 Beta2
classpath 'com.android.tools.build:gradle:3.0.0-beta3'
testCompile 'org.robolectric:robolectric:3.4.2'
Test class that I am using that fails to run:
#Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
#RunWith(RobolectricTestRunner.class)
public class RecipeAdapterTest {
private MainActivity activity;
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
/* Also tried this same Error
activity = Robolectric.buildActivity(MainActivity)
.create()
.resume()
.get();
*/
}
#Test
public void testActivityShouldNotBeNull() {
assertThat(activity, is(notNullValue()));
}
}
This is the stack trace of the error:
android.content.res.Resources$NotFoundException: String resource ID #0x7f0c0020
at android.content.res.Resources.getText(Resources.java:274)
at android.content.res.Resources.getString(Resources.java:360)
at android.content.Context.getString(Context.java:376)
at org.robolectric.shadows.ShadowActivity.getActivityTitle(ShadowActivity.java:100)
at org.robolectric.shadows.ShadowActivity.callAttach(ShadowActivity.java:110)
at org.robolectric.android.controller.ActivityController.attach(ActivityController.java:56)
at org.robolectric.android.controller.ActivityController.of(ActivityController.java:25)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:98)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:94)
at org.robolectric.Robolectric.setupActivity(Robolectric.java:102)
at me.androidbox.busbybaking.adapters.RecipeAdapterTest.setup(RecipeAdapterTest.java:63)
In the Edit Configurations I have set the Working Directory to $MODULE_DIR$
Many thanks for any suggestion.

As mentioned by an engineer from Google team (most possibly Xavier Ducrohet), Robolectric has issues with AAPT2:
Robolectric is not compatible with aapt2.
Two options here.
First option - follow Robolectric guidelines for Android Studio 3.0+
Add the following to your build.gradle:
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
Annotate your test with the Robolectric test runner:
#RunWith(RobolectricTestRunner.class)
public class SandwichTest {
}
Second option: disable AAPT2 adding following line into gradle.properties file:
android.enableAapt2=false

The Robolectric documentation states that the following configuration should be used with Android Studio 3.x:
android {
testOptions {
unitTests.includeAndroidResources true
}
}

(for anyone that might be looking for a solution to a similar problem)
Be sure to use
RuntimeEnvironment.application
and not:
RuntimeEnvironment.systemContext
when you're trying to resolve resources "manually".
That's one case in which Resources$NotFoundException might show up with Robolectric.

Not a direct answer to the question, but if you are testing something that needs a context to query resources against I have found the following to work quite well:
ApplicationProvider.getApplicationContext()
(or RuntimeEnvironment.application -- but this is deprecated in favor of the above)

If your build fails due to an AAPT2 resource processing issue or you want to use Roboelectric, you can disable AAPT2 by setting android.enableAapt2=false in your gradle.properties file and restarting the Gradle daemon by running ./gradlew --stop from the command line.
Official guideline Android Studio 3.0 Release

I was using espresso, and for that you needed to use app resources, not test resources.
So instead of
InstrumentationRegistry.getInstrumentation().context.resources.getString("key")
I used
activityRule.activity.getString("key")

In my case the following solved my issue:
"Problem is related to android studio. Go to 'Run' -> 'Edit configurations...' and change 'Working directory' value to
$MODULE_DIR$
Run your tests.
More info here under 'Building with Android Studio'."
reference: https://github.com/robolectric/robolectric/issues/2653

You can also try #Config(manifest = "<projectFolder>/src/main/AndroidManifest.xml") in the case that you can not simply include the resources as some projects tests will fail with that included.

Related

Robolectric cannot find AndroidManifest.xml

This test originally ran fine. Checked out a new branch several days later (with commits from many other developers) and it no longer works.
Test class in the mylibrary library module:
import com.company.mylibrary.BuildConfig;
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, manifest = "src/main/AndroidManifest.xml", sdk = 21)
public class MyTest {
I have also tried:
#Config(constants = BuildConfig.class, sdk = 21)
#Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21)
In the library module's build.gradle
dependencies {
.
.
testCompile 'org.robolectric:robolectric:3.0'
Error message when running inside AS is:
java.lang.RuntimeException: build/intermediates/manifests/full/debug/AndroidManifest.xml not found or not a file; it should point to your project's AndroidManifest.xml
Error message when running from command line is:
com.company.mylibrary.framework1.feature1.MyTest > testMethod STANDARD_ERROR
java.lang.RuntimeException: build/intermediates/manifests/full/debug/AndroidManifest.xml not found or not a file; it should point to your project's AndroidManifest.xml
A) Don't know why it is looking there for the manifest
B) That file/directory does not exist
C) src/main/AndroidManifest.xml does exist
Things I have tried:
- deleted the build directory in that library module
- restarted Android Studio
- Build/Clean
- Build/Rebuild Project
- run the test (both inside AS and from command line)
- and tried different versions of the #Config notation
Seems to be in a wonky state that I cannot clear.
I am working on a MacBook Pro. Android Studio 2.0 beta5
You need to set the working directory within the test's run configuration to the module directory.
Well, I've tackled the issue you're facing right now several times and found solution suitable for myself.
Generally, if your test logic does not require access to the application's resources, it's worth using usual RobolectricTestRunner as the time of the test execution is relatively shorter comparing it to the test execution time under RobolectricGradleTestRunner.
If, for some reason, you need access to the specific AndroidManifest.xml file, IMO it's better to come up with test file rather than to operate on the project's one.
By saying 'test file' I mean the following:
Let's start by defining what are the methods that can help us to obtain path to the resources files. The goal is to be able execute tests under Android Studio and, what's more relevant, via CLI (gradle :project:testBuildTypeUnitTest)
Java's System class: System.getProperty('user.dir') returns User's current working directory. Obtaining current directory we are in may help us to obtain paths to the resources we need to run our test having them provided.
Overriding RobolectricGradleTestRunner. To create our customized test runner we need the AndroidManifest.xml, the res directory and the assets directory paths:
public class CompassApplicationRobolectricTestRunner extends RobolectricGradleTestRunner {
private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.LOLLIPOP;
private static final int MIN_SDK_VERSION = Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
public CompassApplicationRobolectricTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
protected AndroidManifest getAppManifest(Config config) {
final String manifestPath = PathResolver.resolveAndroidManifestPath();
final String resourcesPath = PathResolver.resolveResPath();
final String assetsPath = PathResolver.resolveAssetsPath();
AndroidManifest manifest = new AndroidManifest(
Fs.fileFromPath(manifestPath),
Fs.fileFromPath(resourcesPath),
Fs.fileFromPath(assetsPath)) {
#Override
public int getTargetSdkVersion() {
return TARGET_SDK_VERSION;
}
#Override
public int getMinSdkVersion() {
return MIN_SDK_VERSION;
}
};
return manifest;
}
}
Below, is the link to the example that worked for me. It was developed, however, some time ago and from the time perspective I see it can be done more elegant way so if you decide to apply this solution to your project, organize your path constants to be static and immutable:
https://github.com/dawidgdanski/android-compass-api/blob/master/app-tests/src/test/java/pl/dawidgdanski/compass/PathResolver.java
It's worth remembering that File.separator returns system's default directories separator. It's extremely useful when it comes to provide system-independent paths separated with default separation symbol.
Eventually, if the solution described above is not the one you want to follow, read decent article about setting up testing environment available here:
http://artemzin.com/blog/how-to-mock-dependencies-in-unit-integration-and-functional-tests-dagger-robolectric-instrumentation/
Hope that solves your problem.
In my case, I was running a single test manually (Right click and run) from inside Android Studio and Roboelectric wanted a RELEASE version. The question above was about debug but my test runs for some reason wanted a release version of the manifiest.
java.lang.RuntimeException: build/intermediates/manifests/release/AndroidManifest.xml not found or not a file; it should point to your project's AndroidManifest.xml
I had never done a production build in this project so that build directory had never been created.
After wrestling for a bit with no success (setting the path in configuration, trying to get the path in my CustomRoboelectric file), I just generated a production build so that I had the release path created with a manifest and everything worked.
So my solution was to just run the build to create what Roboelectric wanted.

AndroidHttpClient not found (when running Robolectric)

I've set up a very simple project to test the integration of Robolectric + Data Binding + Retrolambda. When I run the test suit, I get the following message:
Error:(30, 30) Gradle: error: cannot access AndroidHttpClient
class file for android.net.http.AndroidHttpClient not found
This is pretty odd since I don't use AndroidHttpClient anywhere.
The error occurs here, on the "activity" line:
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class); // Error on this line
textView = (TextView) shadowOf(activity).findViewById(R.id.textView);
button = (Button) activity.findViewById(R.id.button);
editText = (EditText) activity.findViewById(R.id.editText);
}
The program never uses AndroidHttpClient. In fact, this is the entire program:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(new User());
binding.button.setOnClickListener((v) -> {
binding.textView.setText(String.format("Hello, %s!", binding.editText.getText()));
binding.editText.setText("");
});
}
Ideas as to what's wrong?
AndroidHttpClient was removed from the SDK in v23 of the build tools.
As Robolectric is running against earlier versions, it expects it to be there, which is why you're seeing this error.
For now, you can add it back in:
android {
useLibrary 'org.apache.http.legacy'
}
As detailed here.
There is a GitHub ticket open for Robolectric to fix this. You can follow the thread/ticket here.
Update:
As some people have correctly pointed out, a better way of doing this would be to create a class android.net.http.AndroidHttpClient in your test resources. This would be a preferred method because you're only modifying the test sources, not the production code, in order to accommodate the tests.
I've just added fake class android.net.http.AndroidHttpClient in my test sources. And it solved the issue for now. Waiting for Robolectric to be updated
Apparent problem and solution:
AndroidHttpClient was removed from the SDK in API Level 23, while Robolectric was set to run tests with SDK 21:
AndroidHttpClient was removed from the SDK in API Level 23
I was able to solve this problem by creating a new class called AndroidHttpClient within a new package android.net.http. After that I had to annotate my Unit Test class with #Config(constants = BuildConfig.class, sdks = 21) which will run the tests against an emulated version of API 21 which is the last version of Android Robolectric supports currently.
There is currently an issue opened here, so once they release version 3.1 everything should be fine and you won't have to use this workaround.
If target SDK is 28 or greater then according to this, we have to put following line in AndroidManifest.xml
<uses-library android:name="org.apache.http.legacy" android:required="false"/>

Android Unit Tests - no such property: bootClasspath

I'm trying to execute a simple test case for Android following just announced unit testing support - http://tools.android.com/tech-docs/unit-testing-support
After carefully following the walkthrough I'm trying to run ./gradlew test.
I'm getting this error:
Execution failed for task ':app:compileDebugGroovy'.
> No such property: bootClasspath for class: com.android.build.gradle.AppPlugin
while using com.android.tools.build:gradle:1.1.0-rc1.
Anyone else got stuck on that?
The problem is that Groovy Android Gradle plugin (to have Groovy working on Android) isn't simply working with Android plugin in version 1.1.0-rcX.
Here's a very interesting piece of code directly from groovyx.grooid.GroovyAndroidPlugin, version 0.3.5 (current latest, here's the source)
def getRuntimeJars(Project project, plugin) {
int index
switch (getAndroidPluginVersion(project)) {
case ~/0\.9\..*/:
index = 0
break
case ~/0\.10\..*/:
case ~/0\.11\..*/:
case ~/0\.12\..*/:
case ~/0\.13\..*/:
case ~/0\.14\..*/:
case ~/1\.0\..*/:
index = 1
break
default:
index = RUNTIMEJARS_COMPAT.size()-1
}
def fun = RUNTIMEJARS_COMPAT[index]
fun(plugin)
}
and definition of RUNTIMEJARS_COMPAT:
private static List RUNTIMEJARS_COMPAT = [
{ it.runtimeJars },
{ it.bootClasspath }
]
So that API must have changed in Android Gradle between 0.9.x and 0.10.0 (yeah, I know - those Google devs change everything there :[ ). So let's take a look at that problem making class in Android Plugin version 1.0.0:
> javap -cp [path to proper jar] com.android.build.gradle.AppPlugin:
public class com.android.build.gradle.AppPlugin extends com.android.build.gradle.BasePlugin implements org.gradle.api.Plugin<org.gradle.api.Project> {
...
public java.util.List super$2$getBootClasspath();
...
Yup! There's the method we need (coming from parent com.android.build.gradle.BasePlugin class). Now there's nothing like that in version 1.1.0-rc3. What's more, the API of com.android.build.gradle.AppPlugin is completely changed, so it's not a matter of simple if(version) to fix that.
I guess there's no chance to have Groovy Android Gradle plugin working with Unit Tests (since 1.1.0) until authors update the plugin.
Let's wait then.
That Testing Support feature is experimental. That said maybe there is no quick solution to your issue or might be a bug.
However, I would dig deeper into this, reading the message:
This line Execution failed for task ':app:compileDebugGroovy'. mentions the task, so I would go and figure what that task does. I suppose it's a delivered task. The error being that in that task there is a property missing > No such property: bootClasspath for class: com.android.build.gradle.AppPlugin
So maybe try to find that task and make sure the bootClasspath property is set for the AppPlugin class.
Try to upgrade to the new version (RC3)
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0-rc3'
// ..
}
You can also take a look here in order to compare your current setup with a working example.

How can I ignore test failures with the gradle robolectric plugin?

I'm using the robolectric-gradle-plugin for robolectric unit tests. I don't want to fail a build on failed tests. Is there a way in DSL or a property not to fail a test on the build similar to -DtestFailureIgnore=true on the Surefire Maven plugin?
I've tried:
robolectric {
ignoreFailures = true
}
and
robolectric {
ignoreFailure = true
}
and -DignoreFailure=true on the command line.
I can't seem to find any documentation of how to do this, or any reference to ignoring tests in the source code.
answering very old question, so that it might help others who bump into here
testOptions {
unitTests.all {
setIgnoreFailures(true)
}
}
I would suggest not to continue building an APK if there are any failing tests. But if you want to build an APK without testing the only way right now is to use gradle build -x test[1]. This will run build and not run any tests.
[1]http://www.gradle.org/docs/current/userguide/userguide_single.html#sec:excluding_tasks_from_the_command_line
try without '='
robolectric {
ignoreFailures true
}

Running a specific instrumentation unit test with Gradle

Is there a way to run a specific Android instrumentation unit test using Gradle? I've tried
gradle -Dtest.single=UnitTestName connectedInstrumentTest
but it seems to run all the tests in the package.
Using test.single appears to be deprecated. The new correct way to do this is
./gradlew :<module>:test --tests <pattern>
where <pattern> could be something like:
com.example.MyTest to run all test methods in com.example.MyTest
*MyTest to match every method in every class whose name ends with MyTest
*.MyTest.myMethod to run a specific test method in class MyTest in any package
If you have a multi-project build, make sure to give the module path before the test task; otherwise you'll get a misleading error message when it searches for your test pattern in every subproject.
None of this is documented on the Gradle site anywhere I could find it.
This works if you're using an instrumentationTestRunner:
./gradlew test -Pandroid.testInstrumentationRunnerArguments.class=<pkg>.YourClassName
Using gradle 2.10 and android gradle plugin 2.0.0-beta2.
Since you know what test(s) you want to run, you probably know which module / flavor to use too. You can help Gradle out by specifying the exact module and Gradle task. So if your test is in the app module and you want to test the debug flavor:
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=<pkg>.YourClassName
You can get even more fancy with the tests_regex argument instead:
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.tests_regex=PartialClassName*
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.tests_regex=partialMethodName*
The pattern is -D<testTaskName>.single=<TestClass> so in your example it should be:
gradle -DconnectedInstrumentTest.single=UnitTestName connectedInstrumentTest
NOTE: This answer is outdated. You should use the --tests switch in the latest versions of Gradle. (see other answers for an explanation)
Since Android gradle plugin 1.1.0-rc1, one can run single test class using --tests flag by executing:
./gradlew app:testDebug --tests=com.example.MyTest
See http://tools.android.com/tech-docs/unit-testing-support#TOC-Running-from-Gradle
You gotta check this out.
https://github.com/JCAndKSolutions/android-unit-test
I made an issue in this github repository, and this guy solved my problem and upload to maven, so in my build.gradle file I use this plugin.
Instructions are written in his repository. you can easily follow it.
After using this android-unit-test plugin, I can use like
../gradlew -Dtest.single=SomeTest test
or
../gradlew -Dtest.single=SomeTest clean check
Now it's working and I could only run the specific tests I want to
You should not forget to specify a build variant name after test property declaration like
-Dtest<buildVariantName>=<yourTestName>.
Like if you have a debug build type which gives you debug variant after compilation, then if you want to run a test only for this build variant you should declare a command like this:
./gradlew -DtestDebug=UnitTestName testDebug
Erdi's answer didn't work for me but I have a single parent for all my test classes so I was able to do this:
public abstract class BaseEspressoTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
//...
#Override
protected void runTest() throws Throwable {
if(getClass().getSimpleName().equals("MyTestClassName")) {
super.runTest();
}
}
//...
}
This executes only MyTestClassName. We can extend it further to execute only specific test method (or methods):
public abstract class BaseEspressoTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
//...
#Override
protected void runTest() throws Throwable {
if("MyTestClassName".equals(getClass().getSimpleName())
&& "testMethodName".equals(getName())) {
super.runTest();
}
}
//...
}
the Gradle command does not work for me.
I used below mentioened adb command.
for this you need to build your apk first.
adb shell am instrument -w -r -e package -e debug false .debug.test/android.support.test.runner.AndroidJUnitRunner

Categories

Resources