我有一个 fragment ,我正在尝试使用使用 @Singleton api 类的 Robolectric(和 Mockito)进行测试。我正在尝试以一种可以为每个测试自定义响应的方式来模拟单例。这是我的 fragment 引用的 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 一起注入(inject),但我不确定为什么。如果我以错误的方式解决这个问题,请告诉我,但这似乎应该可行。
你们都可以告诉我: 1)为什么我的测试不工作 要么 2)注入(inject)类使用的自定义单例的更好方法?
最佳答案
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)
bind(MyApi.class).to(MyApiImpl.class).in(Singleton.class);
对于测试模块,在你的例子中是 ShadowModule
,设置没有 Singleton
的绑定(bind)。
bind(MyApi.class).to(MyApiImpl.class);
另一种注入(inject)影子类的情况。您应该注入(inject)必须获得注入(inject)成员或具有不同实现的对象。无需注入(inject) ViewGroup
或 Activity
除非有自定义实现的绑定(bind)。
查看第一个 Robolectric 示例:http://pivotal.github.com/robolectric/
只有构造函数,它可以工作。
关于android - 如何使用 robolectric 覆盖注入(inject)的 @Singleton 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13258730/