如何进行单元测试"告诉,不要问"跟随者班?

时间:2012-01-20 08:22:35

标签: java unit-testing tell-dont-ask

我认为这个问题最好用一个例子来解释。

public class MyService {
    private OtherService theOther;
    public void setTheOther(OtherService srv) { theOther = srv; }

    public void myBusinessStuffFor(int id) {
        theOther.applyToAllWith(id, new OtherService.Action() {
            public void apply(Object whatever) {
                doTheHardBusinessStuffWith(whatever);
            }
        }
    }

    private void doTheHardBusinessStuffWith(Object whatever) {
        // here the business stuff provided by MyService
    }
}

public interface OtherService {
    void applyToAllWith(int id, Action action);

    public interface Action {
        void applyOn(Object whatever);
    }
}

我喜欢这种模式,因为它非常有凝聚力。操作界面与其服务配对。许多类中的业务逻辑并不杂乱。子类只为操作提供数据,而不必繁忙。我从这里采用了它(http://jamesladdcode.com/?p=12)。问题是如果我模拟了otherService,我没有找到一个很好的解决方案来测试“doTheHardBusinessStuffWith(Object whatever)”方法中的行为。使用模拟我必须关心如何调用业务方法。但是我该怎么做呢我使用了mockito并且已经使用ArgumentCapture进行了尝试。但由于滥用ArgumentCapture,感觉不对。

我想知道MyService.myBusinessStuffFor(int id)类中使用的模式是否有名称(是策略模式)吗? 但我的主要问题是如何通过模拟OtherService使这段代码可测试?

2 个答案:

答案 0 :(得分:1)

在这种情况下,其他服务实际上不是商业服务。它唯一的职责是从给定的ID中找到对象,并对这些对象应用给定的操作。从功能上讲,这相当于以下代码:

Set<Object> objects = otherService.getObjectsWithId(id);
for (Object o : objects) {
    doTheHardBusinessStuffWith(o);
}

使doTheHardBusinessStuffWith受到保护。为此方法创建单元测试。这是最重要的单元测试:测试业务逻辑的单元测试。

如果你真的想要进行单元测试myBusinessStuffFor,你可以做的是创建一个模拟的OtherService(我的意思是在这里实现这个模拟),它是从一组对象构建的,并应用它给予集合中所有对象的动作。创建MyService的部分模拟,其中doTheHardBusinessStuffWith方法被模拟,并注入了模拟OtherService。在部分模拟上调用myBusinessStuffFor,并验证是否已使用该组对象中的每个对象调用doTheHardBusinessStuffWith

答案 1 :(得分:-1)

你谈论模仿OtherService。我不知道你使用的是哪个模拟框架;但是您应该能够创建一个模拟,它只调用传递给applyToAllWith方法的Action的applyOn方法,并将模拟对象作为参数传递。例如,在mockito中,这将是这样的。

doAnswer( new Answer<Object>(){
    public Object answer( InvocationOnMock invocation ){
        ((Action) invocation.getArguments()[ 1 ]).applyOn( mockObject );
        return null;
}}).when( mockOtherService ).applyToAllWith( anyInt(), any( Action.class ));

其中mockOtherService是您为OtherService界面创建的模拟,而mockObject是您想要传递给doTheBusinessHardStuffWith的模拟。