MVP,测试和私有方法

时间:2012-10-04 02:49:49

标签: java unit-testing tdd mvp

我已经切换到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的presenter.onResume()方法。

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

2 个答案:

答案 0 :(得分: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创建一个新的自选画面。当您在各种方法上调用verify时,如果从未在模拟上调用该方法,则测试将失败。因此,如果您在MyScreen类上调用OnResume,那么您实际上正在编写一个断言的测试,其效果是在其演示者字段上调用doA,doB和doC。

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


顺便提及

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

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

答案 1 :(得分: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确实变得复杂,请将其解压缩到一个单独的类中,对其进行测试,将其注入演示者并测试它是否被调用。