Я провёл модульное тестирование своего класса, и как начать интеграционный тест?

19

Я написал класс, который управляет получателями в списке MailChimp, который называется MailChimpRecipient. Он использует класс MCAPI, который является сторонним API-оболочкой.

http://apidocs.mailchimp.com/api/1.3/ http://apidocs.mailchimp.com/api/downloads/

Я передаю объект MCAPI в конструктор объекта MailChimpRecipient, поэтому я написал модульные тесты с использованием PHPUnit, которые проверяют всю логику в моем собственном классе (я не тестирую класс MCAPI). У меня 100% покрытие кода и все тесты пройдены. Это делается путем насмешки и заглушения объекта MCAPI.

Следующим моим шагом было написание интеграционного теста, также с использованием PHPUnit, где я должен был создать осветитель MailChimpRecipient с использованием реального объекта MCAPI, настроенного на использование реального списка MailChimp.

Я написал то, что я считаю тестом на интеграцию, который в основном запускает тесты с открытым интерфейсом объекта, например:

public function testAddedRecipientCanBeFound()
{
    $emailAddress = 'fred@fredsdomain.com';
    $forename = 'Fred';
    $surname = 'Smith';

    // First, delete the email address if it is already on the list
    $oldRecipient = $this->createRecipient();
    if($oldRecipient->find($emailAddress))
    {
        $oldRecipient->delete();
    }
    unset($oldRecipient);

    // Add the recipient using the test data
    $newRecipient = $this->createRecipient();
    $newRecipient->setForename($forename);
    $newRecipient->setSurname($surname);
    $newRecipient->setEmailAddress($emailAddress);
    $newRecipient->add();
    unset($newRecipient);

    // Assert that the recipient can be found using the same email address
    $this->assertTrue($this->_recipient->find($emailAddress));
}

Тест «интеграция» не проверяет какие-либо внутренние элементы класса - он просто проверяет, что при наличии реального объекта MCAPI он ведет себя так, как объявлено.

Это верно? Это лучший способ запустить тест на интеграцию? В конце концов, внутренности были проверены с модульным тестом. Правильно ли я считаю, что интеграционный тест предназначен для проверки того, что он действительно работает в соответствии с тем, как рекламируется его поведение?

Чтобы продвинуться дальше, класс MailChimpRecipient реализует интерфейс, который также будет реализован другими классами. Идея состоит в том, чтобы использовать фабрику для передачи различных типов объектов получателей списка рассылки в мой код, которые все делают одно и то же, хотя и используют разных поставщиков списков рассылки. Поскольку мои интеграционные тесты тестируют этот интерфейс, как насчет использования его для всех классов, реализующих этот интерфейс? Затем, в будущем, если я создам новый класс, который будет использоваться взаимозаменяемо, я смогу запустить тот же интеграционный тест, прежде чем вставлять его в проект.

Это звучит разумно? Модульные тесты проверяют внутренности объекта, тесты интеграции гарантируют, что он ведет себя так, как объявлено?

Льюис Бассетт
источник
4
Я думаю, что у вас слишком много логики в вашем тесте. Вы запускаете много кода, пока не сделаете утверждение. Вы, вероятно, хотите сначала проверить удаление получателя. Но это не ответ на ваш вопрос, просто комментарий.
2012 года
1
Ну, вы должны использовать эту setUpфункцию, чтобы установить основания для запуска ваших тестов. Если ввод не определен, ну, вы не можете действительно проверить. Вклад должен быть точным, строгим и всегда одинаковым. Если предварительное условие теста не выполнено, вместо этого пропустите тест. Затем проанализируйте, почему он пропускает, и если вам нужно добавить дополнительные тесты и / или setUpне сделано правильно.
2012 года
1
Также не стоит жестко кодировать тестовые значения внутри собственного теста, а сделайте так, чтобы эти члены класса могли совместно использоваться в тестах (и изменяться в центральном месте) или использоваться DataProvider(это функция, предлагающая ввод в качестве параметров для теста).
2012 года
1
Ввод в значении всего, на чем работает ваша тестовая функция. Когда вы тестируете добавление получателя и хотите убедиться, что его уже нет, вы должны, по крайней мере, подтвердить удаление, если оно вступит в силу. В противном случае предварительное условие вашего теста не будет проверяемым.
2012 года
1
+1 за хороший вопрос, но также проголосовал за переход на программистов. Похоже, именно в этом и
заключаются

Ответы:

17

При тестировании вашего кода вы должны обратить внимание на три области:

  • Сценарное тестирование
  • Функциональное тестирование
  • Модульное тестирование

Обычно количество тестов в каждой категории будет иметь форму пирамиды, что означает множество юнит-тестов внизу, несколько функциональных тестов посередине и всего несколько сценариев.

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

С помощью модульного тестирования вы проверяете все возможности, а не только «счастливый путь», но и все условия ошибок.

Если вы абсолютно уверены, что все ваши модули работают изолированно, вы пишете пару тестов (функциональных тестов), чтобы убедиться, что эти блоки также работают при объединении. Затем вы пишете сценарий теста, который проверяет проводку между всеми функциональными модулями.

Например, предположим, что вы тестируете автомобиль.

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

Вместо этого вы бы протестировали небольшую часть двигателя со всеми возможностями (юнит тест)

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

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

Таким образом, в вашем случае вы протестировали все условия ошибок и удачный путь в вашем модульном тесте и знаете, что вам нужно только провести сквозное тестирование с «настоящими компонентами», чтобы проверить правильность подключения.

Несколько других моментов,

  • Избегайте условной логики в вашем модульном тесте. Если вам нужно очиститься, то использование какого-либо глобального состояния и тестов может внезапно повлиять друг на друга.
  • Не указывайте какие-либо данные, которые не имеют отношения к тесту. Если бы я сменил имя или фамилию, провалился бы тест? Возможно, не потому, что важен именно адрес электронной почты, а потому, что вы прямо указали его в своем тесте, я не уверен. Попробуйте взглянуть на шаблон Builder для создания тестовых данных и четко указать, что действительно важно.
Воутер де Корт
источник
Спасибо, это подтверждает многое из того, что я думал. Просто чтобы уточнить - это НЕ юнит тест. Я уже написал модульный тест, который проверяет объект в полной изоляции и имеет 100% покрытие кода объекта. Это должен был быть интеграционный тест, чтобы убедиться, что он работает, когда я внедряю в него настоящий объект MCAPI. Мне просто нужно удалить всех получателей, которые добавлены в список - вот и вся очистка, и она реализована так, чтобы ни один из тестов не влиял друг на друга. Что бы вы предложили вместо этого?
1
Да! Я понял, что вы уже провели юнит-тесты. Отслеживает ли объект MCAPI получателей и необходима ли эта очистка? Если это проблема третьих лиц, вы ничего не можете сделать в интеграционном тесте. Если вы следите за списком, вы должны избегать глобальных данных (и одиночных событий), чтобы убедиться, что тесты не влияют друг на друга. В идеальном мире уборка, когда тест начинается / заканчивается, указывает на недостаток дизайна, но в реальном мире вы не всегда можете избежать этого.
Воутер де Корт
1
Я бы добавил, что тестирование сценариев, вероятно, не совсем то, для чего подходит PHPUnit. Юй может захотеть взглянуть на какой-нибудь инструмент, который вы можете запустить в браузере, например, Selenium, или инструмент, который может имитировать браузер, такой как jMeter.
GordonM
Спасибо ребята! Когда дело доходит до написания хорошего тестируемого кода, наверняка есть чему поучиться, не так ли? Я заказал себе копию этой книги: amazon.co.uk/… . Надеюсь, то, что вы все сказали, станет более понятным после того, как я прочту это. @ Wouter, я просто удаляю получателя, потому что проверка привела бы к добавлению адреса электронной почты в список. Я удаляю его, чтобы этот тест не повлиял на список.
1
@LewisBassett Я не являюсь разработчиком Php, но шаблоны тестирования xUnit ( amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054 ) определенно хорошо читаются. Также статьи на misko.hevery.com/code-reviewers-guide действительно интересны.
Воутер де Корт