Как выполнить юнит-тестирование в классах последователей "скажи, не спрашивай"?

Я думаю, что проблема лучше всего объясняется на примере.

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 был вызван с каждым объектом из набора объектов.

Другие вопросы по тегам