Using Gradle generated Android resources with Robolectric

June 26, 2015

In a previous blog post, I talked about how Gradle allows us to add string resources that can be changed depending on your build configuration, like so:

android {
 buildTypes {
 debug {
 resValue "string", "google_maps_android_api_key", "abcdefghijklm123"
 }
 release {
 resValue "string", "google_maps_android_api_key", "nopqrstuvwxyz456"
 }
 }
}

This generates a string resource that we can then use in our code:

String mapsKey = getString(R.string.google_maps_android_api_key);

However, if you use the Robolectric testing framework (version 2.4) and this line is executed during a test, then you will get the following error in your LogCat:

android.content.res.Resources$NotFoundException: String resource ID #0x7f0c004e

Since this string resource is only generated during a Gradle build and not during Robolectric test execution, Robolectric is not able to find this string resource and this exception is thrown.

The solution

If we attempt to put a dummy string in our strings.xml file…

<string name="google_maps_android_api_key">dummy maps key</string>

…so that this exception doesn’t happen, Robolectric tests will run just fine, but when you build your project it will complain about duplicate resources, as Gradle is trying to generate a resource of the same name.

The solution is to create a shadow resource for this generated string. Create the following class in your test package:

import android.content.res.Resources;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowResources;

/**
 * This class is used to generate Android resources that are otherwise
 * not present during Robolectric tests, such as string resources generated
 * by Gradle with resValue.
 */
@Implements(Resources.class)
public class CustomShadowResources extends ShadowResources {
 @Override
 public CharSequence getText(int id) throws Resources.NotFoundException {
 if (id == R.string.google_maps_android_api_key) return "dummy maps key";

 return super.getText(id);
 }
}

In your test class that is throwing the exception, add a “shadows” annotation parameter to your “Config” annotation that points to your new shadow class:

@Config(
 shadows = CustomShadowResources.class,
 ...
)
@RunWith(RobolectricTestRunner.class)
public class SomeActivityTest {
 ...
}

How it works

The shadow class defines what to do when Robolectric comes across certain types of resources. In this case, when your code tries to access the generated string resource…

String mapsKey = getString(R.string.google_maps_android_api_key);

…Robolectric will use the shadow class’s overridden method to return a dummy string when testing execution comes across use of this string resource, and the Resources$NotFoundException will no longer be thrown.

Joseph Kreiser
Joseph Kreiser
Software Developer

Stay in the loop with our latest content!

Select the topics you’re interested to receive our new relevant content in your inbox. Don’t worry, we won’t spam you.

App Architecture Series: Building a Better User Interface
Development

App Architecture Series: Building a Better User Interface

October 21, 2019

At Michigan Software Labs, we don’t simply write code to satisfy requirements. We find ways to solve problems efficiently and effectively. Knowing that a well-structured application comes down to a set of techniques and patterns, I have been exploring design pattern theories and how they shape our thinking.

Read more
Growing with Glue Work
Team

Growing with Glue Work

July 21, 2021

Co-Written By Kylie Martinez and Amanda Clouser

Read more
The Future Of The Healthcare App: New Ways To Engage Patients And Doctors
Web

The Future Of The Healthcare App: New Ways To Engage Patients And Doctors

April 1, 2020

It wasn't until the recent events surrounding COVID-19 that I began to consider how apps can help improve the healthcare process. As someone who is purely an app user (not a developer), I decided to use some of my social distancing time to research healthcare apps and better understand where the opportunities lie.

Read more
View more articles