I am trying to test push notification with Parse.com using Robolectric. As the initialization has to be done in an Application class, I need to test it. So far, the app is working fine on emulator but I am not able to test it with Robolectric.
My onCreate of application class:
public void onCreate() {
super.onCreate();
// Add your initialization code here
Parse.initialize(this, APP_KEY,
CLIENT_ID);
// Specify an Activity to handle all pushes by default.
PushService.setDefaultPushCallback(this, MainActivity.class);
// Save the current Installation to Parse.
//This is null on test
android_id = Secure.getString(getApplicationContext()
.getContentResolver(), Secure.ANDROID_ID);
System.out.println("android id >>" + android_id);
ParseInstallation installation = ParseInstallation
.getCurrentInstallation();
installation.put("UniqueId", android_id);
installation.saveInBackground();
ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
// If you would like all objects to be private by default, remove this
// line.
defaultACL.setPublicReadAccess(true);
Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);
ParseACL.setDefaultACL(defaultACL, true);
}
Test
@RunWith(RobolectricTestRunner.class)
public class TestParseApplication extends Application {
private ParseApplication parseApplication;
@Before
public void setup() {
parseApplication = new ParseApplication();
parseApplication.android_id = "123";
Robolectric.application = parseApplication;
}
@Test
public void shouldPass() {
assertTrue(true);
}
}
StackTrace
WARNING: no system properties value for ro.build.date.utc
android id >>null
java.lang.IllegalArgumentException: Must subscribe to channel with a valid icon identifier.
at com.parse.PushService.setDefaultPushCallback(PushService.java:298)
at com.parse.PushService.setDefaultPushCallback(PushService.java:277)
at com.parse.starter.ParseApplication.onCreate(ParseApplication.java:30)
at org.robolectric.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:164)
at org.robolectric.RobolectricTestRunner.setUpApplicationState(RobolectricTestRunner.java:430)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
setupActivity() is deprecated in Android unit test. Save this question.
So the main benefit about Robolectric is that it is a lot faster than Espresso or Instrumented tests in general. The downside is that it fakes an Android environment which you should be aware of. To validate real world problems, better use a classic Android Framework approach.
Mockito is used for mocking the dependency which means if you want to access an real object in test environment then you need to fake it or we can say mock it. Now a days it is very easier to do mocking of the objects with Mockito. Roboelectric is the industry-standard unit testing framework for Android.
You should wrap Parse API to some class and mock it:
public class ParseAPI {
public void init(Context context, String key, String id) {
// Add your initialization code here
Parse.initialize(context, key,
id);
}
public void initPush(Context context, Class pushCallbackClass) {
// Specify an Activity to handle all pushes by default.
PushService.setDefaultPushCallback(context, puchCallbackClass);
}
}
Modify your app:
public void onCreate() {
super.onCreate();
ParseAPI api = getParseAPI();
api.init(this, APP_KEY, CLIENT_ID)
api.initPush(this, MainActivity.class);
}
protected ParseAPI getParseAPI() {
return new ParseAPI();
}
Modify your test app and tests:
@RunWith(RobolectricTestRunner.class)
public class TestParseApplication extends ParseApplication {
private ParseApplication parseApplication;
@Before
public void setup() {
parseApplication = new TestParseApplication();
parseApplication.android_id = "123";
Robolectric.application = parseApplication;
}
protected ParseAPI getParseAPI() {
return new ParseAPI() {
public void init(Context context, String key, String id) {
}
public void initPush(Context context, Class pushCallbackClass) {
}
};
}
@Test
public void shouldPass() {
assertTrue(true);
}
}
This should work if I don't make any error while typing.
Of course this is rough idea. It is better to use some mock library instead of writing anonymous classes in tests (My favourite is Mockito
). Please also read about dependency inversion and injection (IoC
). And last it is much better to separate Robolectric test application from application tests (please read http://robolectric.blogspot.nl/2013/04/the-test-lifecycle-in-20.html)
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