5

I am trying to write a JUnit test for an Android Service using RoboGuice 2.0. I have a test module that binds injected dependencies to Mockito mock objects. However, when I run the test, the real implementations from my app module get injected instead. Here is some of the relevant code:

MainApplication.java:

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE,
            RoboGuice.newDefaultRoboModule(this), new MainModule());
        startService(new Intent(this, NotificationService.class));
    }
}

MainModule.java:

public class MainModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(IFooManager.class).to(FooManagerImpl.class).in(Scopes.SINGLETON);
    }
}

NotificationService.java:

public class NotificationService extends RoboService {
    @Inject
    private NotificationManager notificationManager;
    @Inject
    private SharedPreferences prefs;
    @Inject
    private IFooManager fooManager;
    private IFooListener listener = new FooListener();

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        fooManager.addListener(listener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        fooManager.removeListener(listener);
    }

    private class FooListener implements IFooListener {
        // Do stuff that fires Notifications
    }
}

NotificationServiceTest.java:

public class NotificationServiceTest extends ServiceTestCase<NotificationService> {
    @Mock
    private MockFooManager fooManager;
    @Mock
    private MockSharedPreferences prefs;

    public NotificationServiceTest() {
        super(NotificationService.class);
    }

    public void testStart() {
        startService(null);
        verify(fooManager).addListener(isA(IFooListener.class));
    }

    public void testStop() {
        shutdownService();
        verify(fooManager).removeListener(isA(IFooListener.class));
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        MockitoAnnotations.initMocks(this);
        Application app = new MockApplication();
        setApplication(app);
        RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, new TestModule());
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        RoboGuice.util.reset();
    }

    private class TestModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(Context.class).toInstance(getContext());
            bind(IFooManager.class).toInstance(fooManager);
            bind(SharedPreferences.class).toInstance(prefs);
        }
    }
}

MockFooManager and MockSharedPreferences are empty abstract implementations of IFooManager and SharedPreferences, needed because RoboGuice can't inject mocks of interfaces. I am using Mockito with Dexmaker to support bytecode generation for mocked classes. Also, I am not using Robolectric, I am running these tests on a device or in the emulator.

When I run this test, I get the error Wanted but not invoked: fooManager.addListener(isA(com.example.IFooListener)). After stepping through this with the debugger, I found that RoboGuice is injecting the dependencies from MainModule instead of TestModule, so the test is using FooManagerImpl instead of MockFooManager. I don't understand how RoboGuice even knows about MainModule in the test code.

Here are some other things I tried to fix this, but none had any effect:

  • Specify app modules in roboguice.xml instead of calling RoboGuice.setBaseApplicationInjector in MainApplication.onCreate
  • Use Modules.override when calling RoboGuice.setBaseApplicationInjector instead of just passing the module list directly.

How do I get RoboGuice to use TestModule and ignore MainModule in my unit test?

Community
  • 1
  • 1
svattom
  • 320
  • 3
  • 12

2 Answers2

0

It seems like you are missing a call to do the injection in your NotificationServiceTest. This is done as follows:

RoboGuice.getInjector(app).injectMembers(this);

You will need to add this at some point after you set the base injector and before the tests are run.

Suraj C
  • 452
  • 4
  • 10
0

Use

RoboGuice.overrideApplicationInjector(app,RoboGuice.newDefaultRoboModule(app), new TestModule())

instead of

RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, new TestModule());
Noya
  • 3,879
  • 3
  • 26
  • 32