Open KioKrofovitch opened 8 years ago
I never tested it with Robolectric but it doesn't look like a big problem.
You have to make sure that context.getString(R.string.tray__authority)
(here context
is the Application context) returns the correct authority. I'm not sure if Robolectric works with the generated.xml
files which could cause this error.
Please check if mocking the string resource works.
RuntimeEnvironment.application = spy(RuntimeEnvironment.application);
when(RuntimeEnvironment.application.getApplicationContext())
.thenReturn(RuntimeEnvironment.application);
Resources spiedResources = spy(app.getResources());
when(app.getResources())
.thenReturn(spiedResources);
when(spiedResources.getString(R.string.tray__authority))
.thenReturn("my.authority.for.tray");
As @passsy mentioned I guess Robolectric doesn't find a generated String for the authority.
I'm not an Robolectric expert, but you can provide a BuildConfig for it. Make sure you register the correct Provider for Robolectric too. Here is an example (which hopefully works 😄 )
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class MyAwesomeTest {
@Before
public void setUp() throws Exception {
TrayContentProvider provider = new TrayContentProvider();
provider.onCreate();
ShadowContentResolver.registerProvider(BuildConfig.APPLICATION_ID + ".preferences", provider);
}
@Test
public void myTotallyAwesomeTest() throws Exception {
AppPreferences appPreferences = new AppPreferences(RuntimeEnvironment.application);
appPreferences.put("key", "test");
String test = appPreferences.getString("key");
assertThat(test, equalTo("test"));
}
}
Let me know if this works. Note that the parameters may vary in your case. This configuration should be valid for the Tray sample project.
Thank you both so much for your help! I have now successfully moved on from the URI error, but still get an error during setUp
when I attempt to access R.string.tray__authority
.
android.content.res.Resources$NotFoundException: unknown resource 2131034186
I also get the same error if I skip the string resource mocking code and I attempt to run only the code suggested by @jannisveerkamp . I get the error when I call provider.onCreate()
(which ultimately attempts to access R.string.tray__authority
).
Its like Robolectric cannot even see this string resource at all.
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk=21)
public class TrayTest {
@Before
public void setUp() throws Exception {
RuntimeEnvironment.application = spy(RuntimeEnvironment.application);
when(RuntimeEnvironment.application.getApplicationContext())
.thenReturn(RuntimeEnvironment.application);
Resources spiedResources = spy(RuntimeEnvironment.application.getResources());
when(RuntimeEnvironment.application.getResources())
.thenReturn(spiedResources);
when(spiedResources.getString(R.string.tray__authority))
.thenReturn("com.example.tray");
TrayContentProvider provider = new TrayContentProvider();
provider.onCreate();
ShadowContentResolver.registerProvider(BuildConfig.APPLICATION_ID + ".preferences", provider);
}
@Test
public void myTotallyAwesomeTest() throws Exception {
assertTrue(true);
//AppPreferences appPreferences = new AppPreferences(RuntimeEnvironment.application);
//appPreferences.put("key", "test");
//String test = appPreferences.getString("key");
//assertThat(test, equalTo("test"));
}
}
Turns out, Robolectric has a difficult time accessing generated resource values. At first I tried extending RobolectricGradleTestRunner
class to add extra directories to the resource path, but this did not work. Ultimately, I had to create a shadow resource like this:
@Implements(Resources.class)
public class CustomShadowResource extends ShadowResources {
@Override
public CharSequence getText(int id) throws Resources.NotFoundException {
if (id == R.string.tray__authority) return "com.example.tray";
return super.getText(id);
}
}
Then include this Shadow resource in my TrayTest
class like this:
@Config(constants = BuildConfig.class, sdk=21, shadows = CustomShadowResource.class)
While I have resolved the missing string resource issue, the provider registration issue has returned as soon as I attempt to create an AppPreferences
object:
java.lang.IllegalStateException: could not access stored data with uri content://com.example.tray/internal_preferences/com.example/version?backup=true. Is the provider registered in the manifest of your application?
I haven't had the time to look deeper into this. But I suggest to mock the Storage
of Tray
. Instead of TrayPreference
you should work with AbstractTrayPreference
and inject a Storage
which doesn't require a ContenteProvider
. You can use the MockTrayStorage
. Of cause this doesn't allow you to test multiprocess stuff.
I note that mocking tray for tests isn't as simple as it should. #futureimprovement
@KioKrofovitch
@Implements(Resources.class) public class CustomShadowResource extends ShadowResources { @Override public CharSequence getText(int id) throws Resources.NotFoundException { if (id == R.string.tray__authority) return "com.example.tray"; return super.getText(id); } }
I just tried this but it yields a compilation error: the class cannot resolve the override because there doesn't appear to be any getText(int)
method in ShadowResources
. I looked at the robolectric source code and don't see it there either. Am I missing something here? Maybe the API changed?
Edit: OK yes it appears the API changed between 3.0 (the version at the time of your post) and 3.1.
Edit2: I more or less copied the 3.0 implementation and integrated it into the code snippet above - below is working code in 3.8.
@Implements(Resources.class)
public class CustomShadowResources {
@RealObject
private Resources realResources;
public String getText(@StringRes int id) throws Resources.NotFoundException {
if (id == R.string.tray__authority) return "com.example.tray";
CharSequence text = Shadow.directlyOn(realResources, Resources.class).getText(id);
return StringResources.processStringResources(text.toString());
}
}
I'm trying to run Robolectric tests with code that uses Tray, but immediately get the following error for any classes that access
AppPreferences
:java.lang.IllegalStateException: could not access stored data with uri content://com.example.tray/internal_preferences/com.example/version?backup=true. Is the provider registered in the manifest of your application?
Is there a suggested set up for unit testing classes that use Tray? I tried following the existing Tray library unit tests, but ran into issues since
TrayContract
is private scope.