使用 Mockito 的 GWT MVP 测试用例,包含活动和地点以及 GIN
Posted
技术标签:
【中文标题】使用 Mockito 的 GWT MVP 测试用例,包含活动和地点以及 GIN【英文标题】:Test Case for GWT MVP with Activities and Places and GIN using Mockito 【发布时间】:2012-09-06 05:18:59 【问题描述】:我是 GWT MVP 模式的新手,它结合了 GIN 的活动和地点。我已经开始尝试使用 Mockito 为我的 GWT 项目编写 JUnit 测试用例。很多博客建议我不要使用 GWT 测试用例,因为它的性能,所以我打算坚持使用 Mockito。我正在为其中一位演示者编写测试用例。由于我使用 GIN 为 Presenter 中的大多数内容创建实例,因此我必须模拟我的 Gin Injector 对象。我的 Junit 测试用例不允许我模拟 Gin 注射器。我在某处读到我们不能在 Junit 测试用例中使用 Gin,而是必须使用 Guice。我的问题是如何使用 Mockito 模拟我的杜松子酒注射器?我发现一些测试用例使用了与我为我的项目所做的完全相同的模式,只是他们使用了客户端工厂。在测试用例中,我未能用 GIN 替换客户端工厂。我在网上找到的代码如下,我必须在我的测试用例中将 Client Factory 替换为 GIN 注入器。
@RunWith(MockitoJUnitRunner.class) 公共类 ContactListActivityTest
@Mock
private IClientFactory clientFactoryMock;
@Mock
private PlaceController placeControllerMock;
@Mock
private IContactListView contactListViewMock;
@Mock
private AcceptsOneWidget acceptsOneWidgetMock;
@Mock
private IContactServiceAsync contactServiceAsyncMock;
@Mock
private EventBus eventBusMock;
private List<Contact> contacts;
private Contact contact1;
private Contact contact2;
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception
when(clientFactoryMock.getPlaceController()).thenReturn(placeControllerMock);
when(clientFactoryMock.getContactListView()).thenReturn(contactListViewMock);
when(clientFactoryMock.getContactService()).thenReturn(contactServiceAsyncMock);
Answer<Void> answer = new Answer<Void>()
@Override
public Void answer(InvocationOnMock invocation)
Object[] args = invocation.getArguments();
AsyncCallback<List<Contact>> asyncCallback = (AsyncCallback<List<Contact>>) args[0];
contact1 = new Contact();
contact1.setFirstName("Kai");
contact1.setLastName("Toedter");
contact1.setEmail("kai@toedter.com");
contact2 = new Contact();
contact2.setFirstName("Kai2");
contact2.setLastName("Toedter2");
contact2.setEmail("kai2@toedter.com");
final List<Contact> contacts2 = new ArrayList<Contact>();
contacts2.add(contact1);
contacts2.add(contact2);
asyncCallback.onSuccess(contacts2);
return null;
;
doAnswer(answer).when(contactServiceAsyncMock).getAllContacts(any(AsyncCallback.class));
// set the real contacts object, when clientFactory.setContacts is
// called
Answer<Void> setContactsAnswer = new Answer<Void>()
@Override
public Void answer(InvocationOnMock invocation) throws Throwable
contacts = (List<Contact>) invocation.getArguments()[0];
// System.out.println("answer() to setContacts(): " + contacts);
return null;
;
doAnswer(setContactsAnswer).when(clientFactoryMock).setContacts(any(List.class));
// Return the real contacts object, when clientFactory.getContacts is
// called
Answer<List<Contact>> getContactsAnswer = new Answer<List<Contact>>()
@Override
public List<Contact> answer(InvocationOnMock invocation) throws Throwable
return contacts;
;
doAnswer(getContactsAnswer).when(clientFactoryMock).getContacts();
@Test
public void testGotoPlace()
ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(null), clientFactoryMock);
ContactPlace contactPlace = new ContactPlace("kai@toedter.com");
contactListActivity.goTo(contactPlace);
verify(placeControllerMock).goTo(contactPlace);
@Test
public void testStartWithEmptyToken()
clientFactoryMock.setContacts(null); // force RCP
ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(""), clientFactoryMock);
contactListActivity.start(acceptsOneWidgetMock, eventBusMock);
verify(contactListViewMock).setPresenter(contactListActivity);
verify(contactListViewMock).initialize(contacts);
@Test
public void testStartWithToken()
String token = "kai@toedter.com";
clientFactoryMock.setContacts(null); // force RCP
ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(token), clientFactoryMock);
contactListActivity.start(acceptsOneWidgetMock, eventBusMock);
verify(contactListViewMock).setPresenter(contactListActivity);
verify(contactListViewMock).initialize(contacts);
verify(contactListViewMock).selectInitialContact(contact1);
verify(eventBusMock).fireEvent(any(ContactViewEvent.class));
@Test
public void testMayStop()
ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(null), clientFactoryMock);
contactListActivity.start(acceptsOneWidgetMock, eventBusMock);
contactListActivity.mayStop();
verify(contactListViewMock).setPresenter(null);
@Test
public void clientFactoryTest()
List<Contact> testList = new ArrayList<Contact>();
clientFactoryMock.setContacts(testList);
Assert.assertNotNull(clientFactoryMock.getContacts());
请帮忙。
【问题讨论】:
【参考方案1】:如果你的代码依赖于Ginjector
,那么你就有问题了:你没有注入direct dependencies。如果您需要对象工厂,请注入 Provider
。
但在您的情况下,IClientFactory
也可作为List<Contact>
的共享状态、值持有者 或本地缓存 ;这意味着IClientFactory
违反了single responsibility principle。
因此,首先将本地缓存职责提取到它自己的对象(例如ContactListCache
对象,或更通用的ValueHolder<List<Contact>>
)中,然后注入该对象的一个实例。
当然,也可以直接注入PlaceController
、view 和 GWT-RPC 服务。
但实际上,我会更进一步并重构代码以提取 从缓存中检索或询问服务器责任到它自己的 ContactListHolder
对象(或者,当您使用 GWT- RPC,您可以将IContactServiceAsync
接口实现为GWT.create()
生成的接口的包装器,并添加缓存行为;请参阅http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html 以获得一些灵感)。这将大大简化活动的代码。
作为旁注,这段代码可能过多地使用了模拟:为什么不使用真正的PlaceController
(和spy()
ing 它的goTo(Place)
方法)和SimpleEventBus
或CountingEventBus
?
【讨论】:
嗨,Thomas... 我可以从我的代码中删除注入器的间接依赖关系,而只需从注入器发送我需要的实例。我的主要问题是,我是否可以在 Junit 测试用例中使用 Gin。我也是 Junit 测试用例的新手。我不知道我是否可以模拟杜松子酒注射器。这只是我发现的一个例子。我不会使用来自 ClientFactory 或 Gin 注射器的 getContactList。它只是我真正感兴趣的视图实例。非常感谢您的回复。 GIN 注入器只不过是一个接口,因此您可以像其他任何东西一样模拟它。不过,您必须为这些方法提供答案。但正如我所说,你不应该在你的代码中这样做。如果您需要在onModuleLoad
之外的 Ginjector 实例(您不会对其进行单元测试),这是一种代码气味。
嗨,托马斯。我用 Client Factory 替换了 Gin 注射器,我的测试用例运行良好。我说@Mock BodDeskGinjector 注入器的那一刻,测试用例会抛出一个错误,说 java.lang.ExceptionInInitializerError
呃,你有类似BodDeskGinjector INSTANCE = GWT.create(BodDeskInjector.class)
inside 你的BodDeskInjector
接口吗?如果是这样,请将其删除,并将其移至您的 onModuleLoad()
。
嗨 Thomas..谢谢这确实是问题....我修复了它,现在 GWT Ginjector 可以完美地代替 ClientFactory。我在某处读到我们无法在 Junit 中使用 GIN,这导致了所有的混乱。非常感谢所有的帮助.. 干杯【参考方案2】:
我所做的是使用 GwtMockito,它是 Mockito 的扩展。在我使用注射器的唯一一堂课中,我的测试看起来像这样
@RunWith(GwtMockitoTestRunner.class)
public class AppControllerTest
AppController controller;
@Mock
EventBus eventBus;
@Mock
AppInjector injector;
@Before
public void setUp() throws Exception
@Test
public void shouldUseInjector() throws Exception
// Given
controller = new AppController(eventBus, injector);
// When
controller.go();
// Verify
verify(injector).mainPresenter();
【讨论】:
甚至eventBus都可以从injector :S中得到,所以我只要在构造函数中给出injector就足够了以上是关于使用 Mockito 的 GWT MVP 测试用例,包含活动和地点以及 GIN的主要内容,如果未能解决你的问题,请参考以下文章
当我们有 rowmapper 时,如何使用 mockito 编写 junit 测试用例?
如何使用junit和mockito为私有void方法编写测试用例[重复]