使用异步调用测试GWTP presenter

7
我正在使用GWTP,并添加了一个Contract层来抽象Presenter和View之间的知识。我对使用GWTP的结果感到非常满意。我正在使用Mockito测试我的Presenter。
但是随着时间的推移,我发现很难维护干净的Presenter及其测试。我进行了一些重构以改进这一点,但仍然不满意。
我发现以下内容是关键:我的Presenter经常需要异步调用,或者通常需要回调来继续Presenter流程(它们通常是嵌套的)。
例如:
  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));

我将代码进行了优化以使其更简洁(并且在内部不再依赖于参数位置):
doAnswer(new PopulationCallbackAnswer())
  .when(this.populationManager).populate(any(PopulationCallback.class));

因此,当我嘲笑populationManager时,我仍然可以测试我的presenter的流程,基本上就像这样:
@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方法是不是一个好的选择,或者这是一种设计上的问题,是否可以使用更好的设计方法?通常情况下,我的Presenter只是使用其他对象(例如Mediator Pattern)并与视图交互。我有一些Presenter有几百行(约400行)代码。再次提问,这是一个糟糕的设计证明,还是正常的Presenter冗长(因为它使用其他对象)?有人听说过使用GWTP并干净地测试其Presenter的项目吗?希望我已经详细解释了。非常感谢!PS:我对Stack Overflow还很陌生,而且我的英语仍然欠佳,如果我的问题需要改进,请告诉我。
3个回答

1
你可以使用 ArgumentCaptor: 查看此博客文章以获取更多详细信息。

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这样的框架来处理事件。


0

我们在演示器测试中也使用了这个doAnswer模式,通常情况下它都能正常工作。但有一个注意点:如果您像这样进行测试,您实际上是在移除调用的异步性质,也就是说,在服务器调用启动后,回调会立即执行。

这可能会导致未发现的竞争条件。为了检查这些条件,您可以将其变成一个两步过程:在调用服务器时,answer方法仅保存回调。然后,在测试中适当的时候,您可以调用类似flush()或onSuccess()的某些内容来处理您的答案(我建议为此制作一个实用程序类,以便在其他情况下重复使用),以便您可以控制何时真正调用结果的回调。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接