简体   繁体   中英

How to test mock interaction on Activity onResume() Using Dagger Modules and Robolectric?

I'm using Dagger for my dependency injection, it works well in my app but I have trouble to test it. I've followed this pattern in order to create the modules depenency graph : https://github.com/pyricau/shipfaster/blob/master/src/main/java/com/squareup/shipfaster/common/ShipFasterApplication.java

Now, in my MainActivity test class, I want to be able to verify interaction with a mock when the Activity onResume() method is called.

Here is the class :

@Config(emulateSdk = 18)
@RunWith(RobolectricDaggerTestRunner.class)
public class MainActivityTest extends TestCase {

    @Inject MainActivity sut;
    public @Mock MyObject mockMyObject;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        ObjectGraph.create(new TestModule()).inject(this);
    }

    @Test
    public void testThatMyActivityDelegatesDoSomethingToMyObject(){
        //init
        ActivityController<MainActivity> activityController = ActivityController.of(sut);

        //run
        activityController.create().start().resume();

        //verify
        Mockito.verify(mockMyObject).doSomething();
    }

    @Module(
            includes = {ActivityModule.class},
            injects = MainActivityTest.class,
            overrides = true,
            library = true
    )
    class TestModule {
        @Provides
        MyObject provideMyObject() {
            return mockMyObject;
        }
    }
}

From what I can see, the onCreate() method is called but a real instance of myObject is used, not the mocked one. The test failed with a "wanted but not invoked - Actually, there were zero interactions with this mock." error.

Maybe this is because the MainActivity I'm trying to create using Robolectric is not associated with my TestModule because it's created in the Application level, but I manage to make that test to pass by explicitly call a method on the MainActivity and put the myObject.doSomething() in there, but what I need is to test Android lifecycle calls.

Any idea on how can I manage to test this?

The real object is used because I guess you have initialization of ObjectGraph in your Application class. When you call ((Application) getApplication()).inject(this) during tests you are using the same ObjectGraph as when you just run your application.

In this test you are creating completly new ObjectGraph with mock instance of MyObject . This mock is injected only in MainActivityTest because when in MainActivity inject() is called it uses ObjectGraph made in Application .

What you can do is to make TestApplication class (it has to have the same package as your Application class but needs to be in test directory) whitch extends your application and there add your TestModule to override real instances with mocks. For example in this way:

MyApplication.java

package com.example.myapp;

public class MyApplication extends Application {

    ObjectGraph graph;
    private Account currentAccount;

    @Override
    public void onCreate() {
        super.onCreate();
        graph = ObjectGraph.create(getModules().toArray());
        init();
    }

    void init() {
        // initialization stuff should not be called in tests
    }

    List<Object> getModules() {
        List<Object> modules = new ArrayList<>();
        modules.add(new ActivityModule(this));
        return modules;
    }

    public void inject(Object object) {
        graph.inject(object);
    }
}

TestMyApplication.java

package com.example.myapp;

public class TestMyApplication extends MyApplication {

    @Override
    void init() {

    }

    @Override
    List<Object> getModules() {
        modules = super.getModules();
        modules.add(new TestModule());
        return modules;
    }
}

I had the same problems some time ago but I managed to solve it like this: Android Testing with Robolectric and Dagger

WojciechKo suggestion may work in some cases but my solution can work without overriding the Application class in your tests.The difference is that you still need to provide a way to inject Dagger modules in your application rather than instantiating them in the Application class. This way, in your test class you can add the TestModule to override the one that is used in the real Application class.

If you have problems with the solution presented in that link let me know and we can further investigate the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM