我正在使用GWTP,添加一个Contract层来抽象Presenter和View之间的知识,并且我对GWTP的结果非常满意。 我正在用Mockito测试我的主持人。
但随着时间的推移,我发现通过测试很难保持干净的演示者。 我做了一些改进的改进,但我还是不满意。
我发现以下内容是问题的核心: 我的演示者经常需要异步调用,或者通常使用回调调用对象方法来继续我的演示者流程(它们通常是嵌套的)。
例如:
this.populationManager.populate(new PopulationCallback()
{
public void onPopulate()
{
doSomeStufWithTheView(populationManager.get());
}
});
在我的测试中,我结束了验证模拟的PopulationManager对象的population()调用。然后在doSomeStufWithTheView()方法上创建另一个测试。
但是我很快发现这是糟糕的设计:任何改变或重构都会破坏我的大量测试,并迫使我从其他人开始创建,即使演示者功能没有改变! 另外,我没有测试回调是否符合我的要求。
所以我尝试使用mockito doAnswer方法来破坏我的演示者测试流程:
doAnswer(new Answer(){
public Object answer(InvocationOnMock invocation) throws Throwable
{
Object[] args = invocation.getArguments();
((PopulationCallback)args[0]).onPopulate();
return null;
}
}).when(this.populationManager).populate(any(PopulationCallback.class));
我考虑到它的代码更简洁(内部更少依赖于arg位置):
doAnswer(new PopulationCallbackAnswer())
.when(this.populationManager).populate(any(PopulationCallback.class));
因此,在模拟人口管理器时,我仍然可以测试我的演示者的流程,基本上就是这样:
@Test
public void testSomeStuffAppends()
{
// Given
doAnswer(new PopulationCallbackAnswer())
.when(this.populationManager).populate(any(PopulationCallback.class));
// When
this.myPresenter.onReset();
// Then
verify(populationManager).populate(any(PopulationCallback.class)); // That was before
verify(this.myView).displaySomething(); // Now I can do that.
}
我想知道它是否很好地使用了doAnswer方法,或者它是否有代码味道,并且可以使用更好的设计?
通常,我的演示者倾向于使用其他对象(如某些Mediator Pattern)并与视图进行交互。我有一些主持人有几百(~400)行代码。
同样,它是否是一个糟糕设计的证据,或者主持人是否正常(因为它使用其他对象)是正常的吗?
有没有人听说过一些使用GWTP并干净地测试其演示者的项目?
我希望我能以全面的方式解释。
提前谢谢。
PS:我对Stack Overflow很新,加上我的英语仍然缺乏,如果我的问题需要改进,请告诉我。答案 0 :(得分:1)
您可以使用ArgumentCaptor
:
有关详细信息,请查看此blog post。
答案 1 :(得分:0)
如果我理解正确,你会问设计/架构。
这不应算作答案,只是我的想法。
如果我遵循了代码:
public void loadEmoticonPacks() {
executor.execute(new Runnable() {
public void run() {
pack = loadFromServer();
savePackForUsageAfter();
}
});
}
我通常不会指望执行者,只需通过加载和保存来检查方法是否具体。所以这里的执行者只是阻止UI线程中长时间操作的工具。
如果我有类似的话:
accountManager.setListener(this);
....
public void onAccountEvent(AccountEvent event) {
....
}
我会首先检查我们订阅了哪些事件(以及在某些销毁时取消订阅)以及我会检查onAccountEvent
是否有预期的情况。
UPD1。可能在示例1中,更好的是提取方法loadFromServerAndSave
并检查它是否在UI线程上执行,并检查它是否按预期执行所有操作。
UPD2。最好使用像Guava Bus这样的框架进行事件处理。
答案 2 :(得分:0)
我们在演示者测试中也使用了这种doAnswer模式,通常它工作得很好。但有一点需要注意:如果你这样测试,你实际上是删除了调用的异步性质,即在服务器调用启动后立即执行回调。
这可能导致未被发现的竞争条件。要检查这些,您可以将其分为两个步骤:在调用服务器时,answer方法仅保存回调。然后,当你的测试合适时,你可以在你的答案上调用像flush()或onSuccess()那样的东西(我建议为此制作一个可以在其他情况下重复使用的实用程序类),这样你就可以控制实际上调用了结果的回调。