Как unit test логика обратного вызова?

Более полный вопрос заключается в том, что, учитывая зависимость, которая ожидает обратного вызова в качестве параметра, как написать unit test, который охватывает логику обратного вызова и все еще удается макетировать зависимость?

public class DoStuff {
    public void runThis(Runnable callback) {
        // call callback
    }
}

public class ClassUnderTest {
    private DoStuff stuffToDo;

    public void methodUnderTest() {
        this.stuffToDo.runThis(/*a runnable with some logic*/)
    }
}

В приведенном выше примере я бы издевался над stuffToDo, так как я должен проверять вызовы и выписывать выходы вызовов методов. Однако издевательская runThis приводит к тому, что логика обратного вызова не тестируется. Кроме того, логика обратного вызова кажется, что она должна быть частной, поэтому я не ожидал бы ее проверить непосредственно; возможно, это неправильное представление с моей стороны.

Поскольку обратные вызовы используются довольно экстенсивно, я бы ожидал, что их будет распространенным методом для их тестирования, но я его не нашел.

Ответ 1

Вы не можете выполнить ни одного теста. Если вы издеваетесь над чем-то, его издеваетесь, то есть он может только проверять аргументы и моделировать возвращаемые значения (которые вы настраиваете в тесте).

В самом деле, вся точка насмешки над чем-то заключается в том, чтобы проверить, что использует макет в изоляции. Если вы хотите unit test DoStuff, вам не нужно беспокоиться об использовании какой-либо реализации обратного вызова, которая могла бы или не работать. Вы издеваетесь над обратным вызовом, чтобы не беспокоиться об этом.

У вас все еще есть хорошие тесты, проверяя код обратного вызова изолированно и проверяя изолированного пользователя обратного вызова (используя макет для обратного вызова) и, возможно, путем тщательного теста интеграции, где вы используете полностью настроенную компонент в целом.

Ответ 2

Здесь в основном вы хотите протестировать класс DoStuff. Это означает, что вам нужно проверить все методы внутри DoStuff, правильно?. Итак, в этом случае вам нужно сделать вместо того, чтобы издеваться над stuffToDo, вставить издеваемое Runnable в stuffToDo. И затем проверьте, был ли ваш runnable успешно выполнен или нет.

Но если у вас есть другие функции в классе, у вас может быть отдельный тест и издеваться над ними.

Ответ 3

В вашем конкретном случае это звучит так, как будто вы уже протестировали DoStuff (или не волнуйтесь больше, так как это насмехается), и теперь специально тестируют отдельные теги Runnable, которые вы разработали. В этом случае callback звучит точно так, как вы хотите тестировать, таким же образом, кто-то может захотеть специально протестировать стратегию базы данных или стратегию в памяти напрямую.

Если это то, что вы пытаетесь, либо вы можете проверить его в черном ящике, используя его ClassUnderTest, насколько это возможно. Или вы можете создать испытательный жгут на своем конкретном Runnable. Если вы освобождаете этот код и не хотите, чтобы ваш тестовый жгут был доступен, вы можете сделать частные методы проверки и поделиться своими данными специально с вашей сборкой unit test.

Посмотрите здесь для получения информации о том, как создавать сборки для друзей. Обычно я подписываю код unit test, поэтому мне не нужно гадать компилятор командной строки. Я полагаю, это зависит от вашей среды сборки.

Ответ 4

Если вы используете EasyMock, вы можете использовать andStubAnswer для вызова runnable.

doSomethingMock.runThis(runnable);
expectLastCall().andStubAnswer(new IAnserable<Void>() {
   Runnable runnable = (Runnable)getCurrentArguments()[0];
   runnable.run();
   return null;
});

Я полагаю, что другие насмешливые рамки содержат нечто похожее.

Ответ 5

Как насчет поддельного DoStuff, который безоговорочно запускает Runnable?

Затем вы просто ощущаете эффекты - изменения, которые следует соблюдать, если ваш обратный вызов был выполнен.