【问题标题】:How do I override injected @Singleton classes with robolectric?如何使用 robolectric 覆盖注入的 @Singleton 类?
【发布时间】:2012-11-06 20:20:03
【问题描述】:

我有一个片段,我正在尝试使用使用 @Singleton api 类的 Robolectric(和 Mockito)进行测试。我试图以一种可以自定义每个测试的响应的方式来模拟单例。这是我的片段引用的 API 类:

@Singleton
public class MyApi {
    @Inject
    public MyApi(Context context) {
        //Do something
    }

    public MyObject getMyFeed(){
    }
}

这是我正在尝试设置的测试类:

@RunWith(RobolectricTestRunner.class)
public class MyFragmentTest extends TestCase {

    @Inject MyApiInterceptor myApi;
    @Inject Activity shadowActivity;
    @Inject LayoutInflater shadowLayoutInflator;
    @Inject ViewGroup shadowViewGroup;

    @Before
    public void setUp() throws Exception {
        Robolectric.bindShadowClass(SingleThreadActivity.class);
        ShadowApiModule module = new ShadowApiModule();
        Module roboGuiceModule = RoboGuice.newDefaultRoboModule(Robolectric.application);
        RoboGuice.setBaseApplicationInjector(Robolectric.application, RoboGuice.DEFAULT_STAGE,
                roboGuiceModule);
        RoboInjector injector = RoboGuice.getInjector(Robolectric.application);
        injector.injectMembers(this);
    }
    @After
    public void tearDown() {
        RoboGuice.util.reset();
        Application app = Robolectric.application;
        DefaultRoboModule defaultModule = RoboGuice.newDefaultRoboModule(app);
        RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, defaultModule);
    }

    @Test
    public void testOnCreateView() throws Exception{

        myApi.mock = mock(MyApi.class);
        when(myApi.mock.getMyFeed()).thenReturn(new MyObject());

        MyFragment frag = new MyFragment();
        frag.onAttach(shadowActivity);
        frag.onCreate(null);
        frag.onCreateView(shadowLayoutInflator,shadowViewGroup, null);
        frag.onActivityCreated(null);
        frag.onStart();
        frag.onResume();
    }
}

@Singleton
class MyApiInterceptor extends MyApi
{
    public MyApi mock;

    @Inject
    public MyApiInterceptor(Context context) {
        super(context);
    }

    @Override
    public MyObject getMyFeed() throws Exception {
        return mock.getMyFeed();
    }
}
@Implements(Activity.class)
class SingleThreadActivity extends ShadowActivity{

    @Override
    public void runOnUiThread(Runnable action) {
        action.run();
    }
}

class ShadowApiModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Context.class).to(MockContext.class);
        bind(ViewGroup.class).toInstance(mock(ViewGroup.class));
        bind(MyApi.class).to(MyApiInterceptor.class);
    }
}

但是,当我运行测试时,我得到以下信息:

com.google.inject.ConfigurationException: Guice configuration errors:

1) No implementation for android.view.ViewGroup was bound.
  while locating android.view.ViewGroup
    for field at com.mysource.MyFragmentTest.shadowViewGroup(Unknown Source)
  while locating com.mysource.MyFragmentTest

1 error
    at com.google.inject.internal.InjectorImpl.getMembersInjector(InjectorImpl.java:952)
    at com.google.inject.internal.InjectorImpl.getMembersInjector(InjectorImpl.java:957)
    at com.google.inject.internal.InjectorImpl.injectMembers(InjectorImpl.java:943)
    at roboguice.inject.ContextScopedRoboInjector.injectMembersWithoutViews(ContextScopedRoboInjector.java:243)
    at roboguice.inject.ContextScopedRoboInjector.injectMembers(ContextScopedRoboInjector.java:236)
    at com.mysource.MyFragmentTest.setUp(RSSFeedActivityTest.java:58)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:292)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

根据我对 roboguice 的了解,由于某种原因,它似乎无法用 ShadowViewGroup 注入“shadowViewGroup”,但我不知道为什么。如果我以错误的方式解决这个问题,请让我知道,但这似乎应该有效。

你们能不能告诉我: 1)为什么我的测试不起作用 或者 2)注入类使用的自定义单例的更好方法?

【问题讨论】:

    标签: android mockito roboguice robolectric


    【解决方案1】:

    ViewGroup 实现不可见。你在ShadowApiModule 中声明了它,它没有传递给 RoboGuice。

    改为:

    Module roboGuiceModule = RoboGuice.newDefaultRoboModule(Robolectric.application);
    

    试试这个:

    Module roboGuiceModule = Modules.override(RoboGuice.newDefaultRoboModule(Robolectric.application)).with(new ShadowApiModule())
    

    对评论的回答:


    如果您在测试中遇到单例问题,只需将其删除。测试应该是隔离的,所以这里不需要单例模式。

    在生产代码中,代替@Singleton类注解,在Module中设置与Singleton.class的绑定

    bind(MyApi.class).to(MyApiImpl.class).in(Singleton.class);
    

    对于测试模块,在您的情况下为ShadowModule,设置不带Singleton 的绑定。

    bind(MyApi.class).to(MyApiImpl.class);
    

    注入影子类的另一种情况。您应该注入必须获得注入成员或具有不同实现的对象。除非有自定义实现的绑定,否则无需注入 ViewGroupActivity

    看第一个 Robolectric 示例:http://pivotal.github.com/robolectric/
    只有构造函数,它可以工作。

    【讨论】:

    • 这似乎克服了错误,但由于其他影子注入不起作用,我得到了额外的错误。似乎我以某种方式阻止了创建正常的阴影对象。你知道我怎样才能重新启用这些吗?进一步的证据是,当我删除 ShadowApiModule 中的两行以模拟上下文和视图组时,我得到了整个“1)没有绑定 android.view.ViewGroup 的实现。”又报错了。
    • 我没有足够的信息来帮助您解决新问题。我不知道你为什么要注入ActivityViewGroup。我认为你应该研究一下 Robolectric 和 Guice 的工作原理。
    • 我正在注入 Activity 和 ViewGroup,因为我需要它们的影子实例来传递到 Fragment 的 onAttach 和 onCreateView。在我尝试覆盖 @Singleton 版本的 MyApi 之前,这之前有效,所以我知道我打破了它。我不同意我需要更多地研究它。如果您不确定我为什么要以这种方式解决问题,您知道创建此类测试的更好方法吗?
    • 您已经写过其他无效的注射 - 这些信息不足以让我帮助您。我已经编辑了我的答案。
    • 出色的更新答案,我将把功劳归功于你。事实证明,由于我使用的是 RoboSherlock,还有其他问题需要传入的活动需要扩展 RoboSherlockActivity。同样,我更新了我的测试以使用 Mock MyApi,然后将此语句放入我的测试模块中: bind(MyApi.class).toInstance(myMockApiInstance);我仍然没有让它工作,但我正在更新我的测试和代码,事情看起来更积极了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    相关资源
    最近更新 更多