My MainActivity on my Android application checks if the user is logged in (this is stored in SharedPreferences) and if it's not takes the user to the LoginActivity. I am trying to test this using the following code
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private static final int TIME_OUT = 5000; /* miliseconds */
private MainActivity mMainActivity;
private Instrumentation mInstrumentation;
private SharedPreferences mLoginPrefs;
public MainActivityTest() {
super(MainActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mMainActivity = getActivity();
mInstrumentation = getInstrumentation();
mLoginPrefs = mInstrumentation.getTargetContext().getSharedPreferences(LoginActivity.PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = mLoginPrefs.edit();
// User is not logged in, so it should be redirect to LoginActivity
editor.putBoolean("logged_in", false);
editor.commit();
}
//...
public void testC_OpenLoginActivityIfUserIsNotLoggedIn() {
ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
Activity nextActivity = mInstrumentation.waitForMonitorWithTimeout(monitor, TIME_OUT);
assertNotNull(nextActivity);
nextActivity.finish();
SharedPreferences.Editor editor = mLoginPrefs.edit();
// Login the user so we can continue the tests
editor.putBoolean("logged_in", true);
editor.commit();
}
But this doesn't work, the LoginActivity opens but waitForMonitorWithTimeout never returns so I got stuck on LoginActivity (I need to get back to MainActivity to do the other tests).
A code similar to this SO Question works for Button clicks, but this Activity is not loaded by any click so I am thinking maybe there is no time to the monitor to work.
I just need a way to get the actual Activity so I can make an assert and make it finish to continue my tests.
Just one other thing: I would prefer a method without using Robotium if it's possible.
In order to solve your problem, first take a look at the two most important methods for your test:
Instrumentation#addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean)
Instrumentation.ActivityMonitor#waitForActivity()
According to Android API reference:
Add a new Instrumentation.ActivityMonitor that will be checked whenever an activity is started. The monitor is added after any existing ones; the monitor will be hit only if none of the existing monitors can themselves handle the Intent.
Block until an Activity is created that matches this monitor, returning the resulting activity.
Now let's make it a bit more clear.
addMonitor should be called always before the expected activity being started, never too late.
waitForActivity should be called only after the expected activity being started, never too early, since it will block.
Back to your code:
You're calling both of them together, without any magic happening in between. So it's either too late for addMonitor, or too early for waitForActivity.
ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
Activity nextActivity = mInstrumentation.waitForMonitorWithTimeout(monitor, TIME_OUT);
If it's too early for calling waitForActivity, it will block and fail until the timeout (because the expected activity is not hit yet), and you would never see the expected activity being started.
If it's too late for calling addMonitor, the monitoring starts after the expected activity is launched, and the expected activity is not launched again since then, so waitForActivity will block because of no hit of the monitor.
So the difference between the two cases is whether the expected activity is started or not. And for your case, I think it's too late for calling addMonitor.
The solution is very easy: just move addMonitor to a early enough position before your LoginActivity starts, maybe move it to the setUp method, like this:
mInstrumentation = getInstrumentation();
ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
BTW, for your case, with timeout or without timeout doesn't matter.
Don't forget to remove the monitor
after it's not needed anymore e.g:
@Override
protected void tearDown() throws Exception {
mInstrumentation.removeMonitor(monitor);
super.tearDown();
}