10

Attempting to write my first Android-by-TDD app (I've written a few small Android apps without TDD, so am familiar with the environment), but I can't seem to get my head around how to write my first test.

The scenario:

I have an activity, TasksActivity, and a service, TasksService. I need to test that TasksActivity starts TasksService in its onStart() method.

The test I've written is this:

public class ServiceControlTest extends ActivityUnitTestCase<TasksActivity>{
public ServiceControlTest() {
    super(TasksActivity.class);
}

public void testStartServiceOnInit () {
    final AtomicBoolean serviceStarted = new AtomicBoolean(false);
    setActivityContext(new MockContext() {
        @Override
        public ComponentName startService(Intent service) {
            Log.v("mockcontext", "Start service: " + service.toUri(0));
            if (service.getComponent().getClassName().equals (TasksService.class.getName()))
                serviceStarted.set(true);
            return service.getComponent();
        }
    });
    startActivity(new Intent(), null, null);
    assertTrue ("Service should have been started", serviceStarted.get());
}           
}

In my onCreate() method in TasksActivity I have:

    startService(new Intent(this, TasksService.class));

I have also tried

    getBaseContext().startService(new Intent(this, TasksService.class));

But in neither case does my MockContext's startService method get called. Is there a way I can set up interception of this method? I'd really rather not have to start wrapping the basic Android APIs in order to perform such basic tests...

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
Jules
  • 14,841
  • 9
  • 83
  • 130
  • Have you verified that your `Activity`'s `onCreate()` method is getting called through the instrumentation? I don't see anything necessarily wrong with what you're doing there. – Brian Dupuis Feb 15 '12 at 14:12
  • Now, that's interesting. It isn't. Nor is it called if I explicitly do getInstrumentation().callActivityOnCreate(...). But it *is* called if I comment out my mock context... there must be some dependency on the context doing something or other in order to pass the call through. – Jules Feb 15 '12 at 15:23
  • Yup. Found this (http://www.paulbutcher.com/2011/03/mock-objects-on-android-with-borachio-part-2/), take a look. Essentially, `MockContext` is almost entirely useless :). – Brian Dupuis Feb 15 '12 at 15:38
  • Yeah, I just found the same article, and switching to ContextWrapper has solved the problem. Thanks for the help. :) – Jules Feb 15 '12 at 15:50

1 Answers1

7

Just to summarise the conversation with Brian Dupuis in comments, the problem was that MockContext doesn't provide facilities that are required by the test instrumentation in order to correctly call onCreate(). Switch from MockContext to ContextWrapper solved this problem.

The working test therefore looks like this:

public void testStartServiceOnInit () {
    final AtomicBoolean serviceStarted = new AtomicBoolean(false);
    setActivityContext(new ContextWrapper(getInstrumentation().getTargetContext()) {
        @Override
        public ComponentName startService(Intent service) {
            Log.v("mockcontext", "Start service: " + service.toUri(0));
            if (service.getComponent().getClassName().equals ("net.meridiandigital.tasks.TasksService"))
                serviceStarted.set(true);
            return service.getComponent();
        }
    });
    startActivity(new Intent(), null, null);
    assertTrue ("Service should have been started", serviceStarted.get());
}
Jules
  • 14,841
  • 9
  • 83
  • 130
  • 1
    With the deprecation of ActivityTestCase and MockContext, do have an alternative to the original solution? Thanks! – Johnny Wu Nov 07 '16 at 04:37