-1

我已经切换到 MVP 架构并正在尝试对其进行测试。我对如何构建我的 Presenter 有一些疑问。

public class Presenter
{
    public void onResume()
    {
        doA();
        doB();
        doC();
    }

    protected void doA() {};
    protected void doB() {};
    protected void doC() {};
}

public class MyScreen implements MyScreenView
{
    private Presenter presenter;

    public MyScreen()
    {
        presenter = new Presenter(this);
    }

    public OnResume()
    {
        presenter.OnResume();
    }
}

在上面的代码中,当视图恢复时,视图只是调用了演示者 OnResume()。演示者在内部调用 3 个方法,doA()、doB() 和 doC() 来做它必须做的任何事情。

或者,我可以直接在视图中调用 doA()、doB() 和 doC(),如下所示:

public class Presenter
{
    public void doA() {};
    public void doB() {};
    public void doC() {};
}

public class MyScreen implements MyScreenView
{
    private Presenter presenter;

    public MyScreen()
    {
        presenter = new Presenter(this);
    }

    public OnResume()
    {
        presenter.doA();
        presenter.doB();
        presenter.doC();
    }
}

我的问题是,测试第二个解决方案对我来说更容易,因为演示者被干净地分解为 3 个单独的个人责任公共方法,我可以分别为 doA、doB 和 doC 编写测试,而不是为演示者编写一个整体测试。解决方案1的onResume()方法。

在第一个解决方案中,我必须为 onResume() 编写测试,它共同负责调用这 3 个方法。这意味着它更难测试,因为它比调用其他私有函数的函数更容易测试由个人负责的较小函数。然而,在我看来,第二个解决方案并不像一个好的 MVP,因为它似乎知道演示者在做什么,只是不让演示者做它在第一个解决方案的 onResume() 方法中应该做的事情。

4

2 回答 2

1

如果您将 MyScreen 类更改为

public class MyScreen implements MyScreenView
{
    private presenter;

    public MyScreen()
    {
        presenter = new Presenter();
    }

    public MyScreen(Presenter p) 
    {
        presenter = p;
    }

    public OnResume()
    {
        presenter.OnResume();
    }
}

然后你有一个生产构造函数,它创建一个新的 Presenter 和一个用于测试的便利构造函数,因此你可以使用 Mock Presenter 实例化它。我更喜欢 Mockito(内存中的代码,所以......)

Presenter mockP = Mock(Presenter.class);
MyScreen target = new MyScreen(mockP);
target.OnResume();
verify(mockP).doA();
verify(mockP).doB();
verify(mockP).doC();

在这里,您使用 Mock Presenter 创建一个新的 MyScreen。当您对各种方法调用 verify 时,如果从未在 mock 上调用该方法,您的测试将失败。因此,您实际上是在编写一个测试,断言如果您在 MyScreen 类上调用 OnResume,效果就是在其 Presenter 字段上调用 ​​doA、doB 和 doC。

您可以在验证方法被调用时指定您期望的参数。并且您可以存根 mockP 的方法,以便它在特定场景中返回已知结果。


顺便

public OnResume()
    {
        presenter.OnResume();
    }

恕我直言是您的两个选项中更好的选择,因为这样您就可以更改该presenter.OnResume()方法的功能而无需更改 MyScreen 类。

于 2012-10-04T11:45:43.210 回答
1

首先在 My Screen 类中使演示者可注入

public class MyScreen implements MyScreenView
{
    private presenter;

    public MyScreen()
    {
        presenter = this(new Presenter());
    }

    public MyScreen(Presenter p) 
    {
        presenter = p;
    }

    public OnResume()
    {
        presenter.OnResume();
    }
}

现在您可以通过注入模拟来测试 MyScreen 的行为。

Presenter mockP = Mock(Presenter.class);
MyScreen target = new MyScreen(mockP);
target.OnResume();
verify(mockP).doA();
verify(mockP).doB();
verify(mockP).doC();

在完全独立的测试中,您可以测试演示者:

Presenter presenter = new Presenter();
presenter.onResume();
assertEquals("some State that should be true after calling on resume", presenter.getSomeMagicState());

不要测试 onResume 调用 doA、doB 和 doC,通过检查演示者的状态来测试方法的行为。

如果 doX 所做的事情变得复杂,请将其提取到一个单独的类中,对其进行测试,将其注入演示者并测试它是否被调用。

于 2012-10-04T11:58:37.127 回答