我认为这个问题最好用一个例子来解释。
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使这段代码可测试?
答案 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
的模拟。