Как выполнить юнит-тестирование в классах последователей "скажи, не спрашивай"?
Я думаю, что проблема лучше всего объясняется на примере.
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). Проблема в том, что я не нашел хорошего решения для тестирования поведения в методе doTheHardBusinessStuffWith(Object независимо от того, что), если я высмеиваю другой сервис. С издевательством мне нужно заботиться о том, как вызывается бизнес-метод. Но как я могу это сделать. Я использую mockito и уже попробовал его с ArgumentCapture. Но это не правильно из-за злоупотребления ArgumentCapture.
Я хотел бы знать, имеет ли имя шаблон, используемый в классе MyService.myBusinessStuffFor(int id) (это шаблон стратегии)? Но мои основные вопросы - как сделать этот код тестируемым с помощью макета OtherService?
2 ответа
Вы говорите о насмешках над другим сервисом. Я не знаю, какую систему насмешки вы используете; но вы должны иметь возможность создать макет, который просто вызывает метод applyOn действия, который передается методу applyToAllWith, передавая макет объекта в качестве аргумента. Например, в 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
,
В этом случае другой сервис не является бизнес-сервисом. Его единственная обязанность - найти объекты с заданным идентификатором и применить данное действие к этим объектам. Функционально это эквивалентно следующему коду:
Set<Object> objects = otherService.getObjectsWithId(id);
for (Object o : objects) {
doTheHardBusinessStuffWith(o);
}
Делать doTheHardBusinessStuffWith
защищенный. Создайте модульный тест для этого метода. Это самый важный модульный тест: тот, который проверяет бизнес-логику.
Если вы действительно хотите юнит-тест myBusinessStuffFor
то, что вы могли бы сделать, это создать ложный OtherService (и я имею в виду реализовать его сами), который построен из набора объектов и применяет данное действие ко всем объектам в наборе. Создать частичный макет MyService
где doTheHardBusinessStuffWith
метод является насмешливым, а тот, который вводится вами, - имитирующим OtherService. Вызов myBusinessStuffFor
на частичной макете, и убедитесь, что doTheHardBusinessStuffWith
был вызван с каждым объектом из набора объектов.