【问题标题】:How to test mock interaction on Activity onResume() Using Dagger Modules and Robolectric?如何使用 Dagger 模块和 Robolectric 在 Activity onResume() 上测试模拟交互?
【发布时间】:2014-12-15 13:55:42
【问题描述】:

我正在使用 Dagger 进行依赖注入,它在我的应用程序中运行良好,但我无法对其进行测试。我遵循这个模式来创建模块依赖图:https://github.com/pyricau/shipfaster/blob/master/src/main/java/com/squareup/shipfaster/common/ShipFasterApplication.java

现在,在我的 MainActivity 测试类中,我希望能够在调用 Activity onResume() 方法时验证与模拟的交互。

这是课程:

@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;
        }
    }
}

据我所知,调用了 onCreate() 方法,但使用了 myObject 的真实实例,而不是模拟的实例。测试失败并显示“需要但未调用 - 实际上,与此模拟的交互为零。”错误。

这可能是因为我尝试使用 Robolectric 创建的 MainActivity 与我的 TestModule 没有关联,因为它是在应用程序级别创建的,但我设法通过显式调用 MainActivity 上的方法并放置myObject.doSomething() 在那里,但我需要的是测试 Android 生命周期调用。

知道如何测试这个吗?

【问题讨论】:

  • 您的MainActivity sut 实例来自哪里?是否有提供此类的另一个模块?你什么时候将MyObject 实例注入sut
  • 在上面的代码示例中,MainActivity 是使用@Inject 注入的,它不是由任何模块提供的,dagger 只会使用默认构造函数。我还尝试通过 MainActivity sut = new MainActivity() 在我的 setup() 方法中创建它,它没有任何区别。在 MainActivity 中,myObjected 由 Dagger 注入:public class MainActivity extends BaseActivity { @Inject MyObject myObject; ...
  • 好的,我假设您的 @Inject 构造函数上有 @Inject 注释。您具体在哪里将MyObject 注入MainActivity?每当 Dagger 使用默认构造函数实例化对象时,它不会注入其字段。
  • @Inject 不在构造函数中,而是在字段声明中,不,使用 Dagger,您无需专门将对象注入 Activity,Dagger 会自动为您完成。您唯一需要做的就是创建依赖关系图。我在 MainActivity ((Application) getApplication()).inject(this);onCreated() 方法中执行此操作。提醒一下,我的代码功能齐全,编译和工作都很好,只有单元测试是个问题。
  • 你是对的,我错了。

标签: java android unit-testing robolectric dagger


【解决方案1】:

使用真实对象是因为我猜你在 Application 类中初始化了 ObjectGraph。当您在测试期间调用 ((Application) getApplication()).inject(this) 时,您使用的 ObjectGraph 与您刚刚运行应用程序时相同。

在此测试中,您将使用 MyObject 的模拟实例创建全新的 ObjectGraph。此模拟仅在 MainActivityTest 中注入,因为在 MainActivity 中调用 inject() 时,它使用在 Application 中制作的 ObjectGraph

您可以做的是创建TestApplication 类(它必须与您的Application 类具有相同的包,但需要在测试目录中)扩展您的应用程序并在那里添加您的TestModule 以覆盖真实带有模拟的实例。比如这样:

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;
    }
}

【讨论】:

  • 这是一个非常好的观点。我试了一下,但没有任何区别。实际上,我仔细研究了 Dagger 中的模块覆盖。我的 TestModule 通过重新定义一些提供方法(例如provideMyObject())来覆盖我的模块。它实际上工作正常,如果我在 sut 上调用显式方法,我可以验证我的模拟是否像 MODULE OVERRIDES 部分中的 Dagger Documentation 一样被调用。我认为我的问题更多地与 Robolectric 以及如何模拟活动生命周期有关。
  • 你试过这样获取Activity吗:Robolectric.buildActivity(MyActivity.class).create().start().resume().get()
  • 是的,我有。在这种情况下,主要活动是由 Robolectric 以某种方式创建的,并且出于某种原因它使用了真正的 Dagger 模块。我可以看到调用了 onResume(),但是使用的是 MyObject 的真实实例,而不是模拟实例。我想知道解决方案是否可以以某种方式在默认构造函数中初始化 objectGraph,仅用于测试,以确保真正的模块被测试覆盖。不完全确定如何做到这一点......
【解决方案2】:

前段时间我也遇到过同样的问题,但我设法解决了这个问题: Android Testing with Robolectric and Dagger

WojciechKo 的建议在某些情况下可能有效,但我的解决方案可以在不覆盖测试中的 Application 类的情况下工作。不同之处在于,您仍然需要提供一种在应用程序中注入 Dagger 模块的方法,而不是在 Application 类中实例化它们.这样,在您的测试类中,您可以添加 TestModule 以覆盖在实际应用程序类中使用的模块。

如果您对该链接中提供的解决方案有疑问,请告诉我,我们可以进一步调查问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-01
    • 2018-07-07
    • 2012-05-30
    • 2011-08-03
    相关资源
    最近更新 更多