Java 如何在“中进行单元测试”;告诉我,唐';“不要问”;追随者阶级?

Java 如何在“中进行单元测试”;告诉我,唐';“不要问”;追随者阶级?,java,unit-testing,tell-dont-ask,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 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);
    }
}
我喜欢这种模式,因为它很有凝聚力。操作接口与其服务配对。业务逻辑在许多类中并不杂乱无章。子类只为操作提供数据,不必很忙。我从这里领养了它()。问题是,如果我模仿otherService,我没有找到测试“doTheHardBusinessStuffWith(Object Anwhere)”方法中行为的好方法。对于mock,我必须关心如何调用业务方法。但我该怎么做呢。我使用mockito并已经用ArgumentCapture进行了尝试。但因为滥用权力,感觉不太对

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

您谈论的是模仿OtherService。我不知道你使用的是哪种模拟框架;但是您应该能够创建一个mock,它只调用传递给applyToAllWith方法的操作的applyOn方法,并将一个mock对象作为参数传递。例如,在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 ));
doAnswer(新答案(){
公共对象应答(调用锁调用){
((操作)invocation.getArguments()[1]).applyOn(mockObject);
返回null;
}}).when(mockOtherService).applyToAllWith(anyInt(),any(Action.class));

其中,
mockOtherService
是您为
OtherService
接口创建的mock,而
mockObject
是您希望传递给
doTheBusinessHardStuffWith
的任何mock,在这种情况下,另一个服务不是真正的业务服务。它唯一的职责是从给定的ID中查找对象,并对这些对象应用给定的操作。在功能上,这相当于以下代码:

Set<Object> objects = otherService.getObjectsWithId(id);
for (Object o : objects) {
    doTheHardBusinessStuffWith(o);
}
Set objects=otherService.getObjectsWithId(id);
用于(对象o:对象){
是否与(o)有关;
}
使对硬件业务进行保护。为此方法创建单元测试。这是最重要的单元测试:测试业务逻辑的单元测试


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

到目前为止,不允许对doTheHardBusinessStuffWith进行保护。这是私人行为,应该保持隐私。myBusinessStuffFor是此类的入口点,因此应该对其进行单元测试。对象收集行为正是我想要隐藏的。您必须始终运行两次循环—收集原始对象,然后应用业务逻辑。只有OtherService知道它可以处理多个对象。在其他用例中,将OtherService作为适配器实现以调用该操作可能会变得很困难。然后将其作为
myBusinessStuffFor
测试的一部分进行测试。但是你让你的生活变得很艰难。这正是我想要的。如果有人能告诉我这个模式是如何命名的,那么我会非常满意。对不起,我不太擅长知道这些东西的名称。在我看来,这只是测试代码的常识性方法;我不知道这个特定模式有什么名字。是的,但我觉得“命令模式”比这个更一般。这只是“命令模式”在单元测试中的一个应用,但我忍不住认为OP是在一个更具体的名称之后。也许不是,哈哈。为什么我总是斜着思考:-)。命令模式似乎真正描述了我的业务方法行为。我总是想到“行为注射”之类的东西。谢谢你帮了我两个大忙。