Итак, первая проблема, с которой я столкнулся, - это создание модульного теста для метода void.
Метод void подразумевает коллабораторов.Вы проверяете это.
Пример.Предположим, нам нужна была задача, которая скопировала бы System.in
в System.out
.Как бы мы написали автоматизированный тест для этого?
void copy() {
// Does something clever with System.in and System.out
}
Но если немного покоситься, вы увидите, что у вас действительно есть код, который выглядит как
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
// Does something clever with `in` and `out`
}
Если мы выполними извлеките рефакторинг метода для этого, тогда мы можем получить код, который выглядит как
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
copy(in, out);
}
void copy(InputStream in, PrintStream out) {
// Does something clever with `in` and `out`
}
Последний из них - это API, который мы можем протестировать - мы настраиваем соавторов, передаем их в тестируемую системуи проверьте изменения позже.
На данный момент у нас нет теста для void copy()
, но это нормально, так как код «настолько прост, что явно нет недостатков».
Обратите внимание на то, что с точки зрения теста между этими схемами нет большой разницы
{
Task task = new Task();
task.copy(in, out);
}
{
Task task = new Task(in, out);
task.copy();
}
{
Task task = Task.createTask();
task.copy(in, out)
}
{
Task task = Task.createTask(in, out);
task.copy();
}
Можно подумать об этом так: мы не пишемСначала API, мы сначала пишем test .
// Arrange the test context to be in the correct initial state
// ???
// Verify that the test context arrived in final state consistent with the specification.
То есть, прежде чем начать думать об API, сначала нужно разобраться, как вы собираетесь оценить результат.
сбПо-моему, другое написание: если эффекты вызова функции не обнаружимы, то вы можете просто отправить неоперативный код.Если неоперация не соответствует вашим требованиям, то где-то должен наблюдаться наблюдаемый эффект - вам просто нужно выяснить, наблюдается ли этот эффект непосредственно (проверка возвращаемого значения) или по доверенности (проверка эффекта на некоторыхдругой элемент в решении, или тестовый двойник, играющий роль этого элемента).
Хорошо, теперь я могу передать params методу для его тестирования, но что я могу проверить?
Вы проверяете, что он должен делать.
Попробуйте этот мысленный эксперимент - предположим, что вы и я спаривались, и вы предложили этот интерфейс
interface Task {
void lookForChanges();
}
и затем, после некоторого тщательного размышления, я реализовал это:
class NoOpTask implements Task {
@Override
void lookForChanges() {}
}
Как бы вы продемонстрировали, что моя реализация не удовлетворяет требованиям?
То, что вы написали в вопросе, было «оно обновляет базу данных и отправляет JMS-сообщение клиенту», поэтому необходимо рассмотреть два утверждения: обновилась ли база данных и было ли отправлено сообщение JMS?
Все выглядит примерно так
Given:
A database with data `A`
A webservice with data `B`
A JMS client with no messages
When:
The task is connected to this database, webservice, and JMS client
and the task is run
Then:
The database is updated with data `B`
The JMS client has a message.
Похоже, то, что вы предлагаете, является сквозным тестом.
Этовыглядит как один.Но если вы используете удвоение теста для этих сотрудников, а не живых систем, тогда ваш тест выполняется в изолированной и детерминированной оболочке.
Это, вероятно, общительный тест - тест не знает или не заботится о реализацииподробности тестируемой системы в предложении when.Я не утверждаю, что SUT является единым целым и единым целым.
Сначала я должен увидеть реализацию foo.Я ошибся?
Да - вам нужно понимать спецификацию foo, а не реализацию.