Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 950
rontho Avatar asked Nov 09 '22 21:11

rontho


1 Answers

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;
    }
}
like image 62
WojciechKo Avatar answered Nov 14 '22 23:11

WojciechKo