I have currently inherited an Android application that has zero code coverage in it and my first job is to get some unit tests written for it. So I have decided to also use it as an opportunity to learn Robolectric.
However I am hitting initial issues getting two simple dummy tests to run.
Here is my code in my test file:
@Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class)
public class SplashActivityTest {
private SplashActivity activity;
// @Before => JUnit 4 annotation that specifies this method should run before each test is run
// Useful to do setup for objects that are needed in the test
@Before
public void setup() {
// Convenience method to run SplashActivity through the Activity Lifecycle methods:
// onCreate(...) => onStart() => onPostCreate(...) => onResume()
activity = Robolectric.setupActivity(SplashActivity.class);
}
// @Test => JUnit 4 annotation specifying this is a test to be run
// Checking that the UI gets setup correctly
@Test
public void dummy() {
String test = "POP!";
assertTrue("POP!",
test.equals("POP!"));
}
@Test
public void dummyTwo() {
String test = "POP!!";
assertTrue("POP!!",
test.equals("POP!!"));
}
}
The problem is the activity extends another class called baseactivity and in this class a custom Application class is used.
In this custom application class Picasso is created as a Singleton using the following code:
picasso = new Picasso.Builder(getApplicationContext()).downloader(new OkHttpDownloader(picassoClient)).build();
Picasso.setSingletonInstance(picasso);
When I run the tests I get the following error:
java.lang.IllegalStateException: Singleton instance already exists. at com.squareup.picasso.Picasso.setSingletonInstance(Picasso.java:677)
So it looks like the application class is getting created twice, once for each test as with one test it runs fine. So I am assuming my pattern for testing is wrong here? Can anyone help me out with a correct pattern? As with an Unit test I want to just test limited functionality so I am not sure what I am doing is correct.
EDIT: I have tried to setup a "mock" application class and get Robolectric to use it but it still seems to use the real Application class.
So in test/java I have the following class:
public class TestMyApplication extends MyApplication
implements TestLifecycleApplication {
@Override
public void onCreate() {
super.onCreate();
initPicasso();
}
@Override
protected void initPicasso() {
//nothing to do
}
@Override public void beforeTest(Method method) {
}
@Override public void prepareTest(Object test) {
}
@Override public void afterTest(Method method) {
}
}
As you can see it extends the MyApplication class which is in my main app and I have also added @Override to the initPicasso method to try to stop it getting called, however when I run my tests I still get the error where the Picasso Singleton is set a second time for the second test.
So when I run my test class it still goes into the Application class in my main app, why does Robolectric do this when Unit tests should be limited in scope?
I have also tried this:
@Config(constants = BuildConfig.class, application = TestMyApplication.class)
But when I try this and run the test class I get an error saying it can't find TestMyApplication, so its a tearing my hair out issue as to why Robolectric wont use my mocked Application class.
To get it to work I had to create the following class which extends the RobolectricGradleTestRunner and force it to use the TestMyApplication class.
public class TestRunner extends RobolectricGradleTestRunner {
public TestRunner(final Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected Class<? extends TestLifecycle> getTestLifecycleClass() {
return MyTestLifecycle.class;
}
public static class MyTestLifecycle extends DefaultTestLifecycle {
@Override
public Application createApplication(final Method method, final AndroidManifest appManifest, final Config appConfig) {
// run tests under our TestApplication
return new TestMyApplication();
}
}
}
Then in the TestMyApplication class I had to override the initPicasso method:
@Override protected void initPicasso(){
//do nothing
}
Only after doing this did Robolectric finally skip the initPicasso in the main MyApplication.java class.
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