Модульные тесты - это те, которые проверяют небольшой фрагмент кода (в основном одиночные методы) .
Интеграционные тесты - это тесты, которые проверяют взаимодействие между несколькими областями кода (которые, надеюсь, уже имеют свои собственные модульные тесты). Иногда части тестируемого кода требуют, чтобы другой код действовал определенным образом. Вот тут-то и пригодятся Mocks & Stubs. Итак, мы имитируем / делаем заглушку для части кода, чтобы она работала очень конкретно. Это позволяет нашему интеграционному тесту работать предсказуемо без побочных эффектов.
Все тесты должны выполняться автономно без совместного использования данных. Если совместное использование данных необходимо, это признак того, что система недостаточно разобщена.
При взаимодействии с внешним API (в частности, RESTful API, который будет изменять данные в реальном времени с помощью запроса POST), я понимаю, что мы можем (должны?) Смоделировать взаимодействие с этим API (более красноречиво сказано в этом ответе ) для теста интеграции. Я также понимаю, что мы можем провести модульное тестирование отдельных компонентов взаимодействия с этим API (создание запроса, анализ результата, выдача ошибок и т. Д.). Я не понимаю, как это сделать.
Как проверить свое взаимодействие с внешним API, имеющим побочные эффекты?
Прекрасным примером является Content API Google для покупок . Чтобы иметь возможность выполнить поставленную задачу, требуется приличный объем подготовительной работы, затем выполнение фактического запроса, а затем анализ возвращаемого значения. Некоторые из них без какой-либо «песочницы» .
Код для этого обычно имеет несколько уровней абстракции, например:
request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Примечание: это пример PHP5 / PHPUnit
Учитывая, что testTheRequest
- это метод, вызываемый набор тестов, пример выполнит живой запрос.
Теперь этот оперативный запрос (надеюсь, при условии, что все прошло хорошо) будет выполнять POST-запрос, побочным эффектом которого является изменение оперативных данных.
Это приемлемо? Какие у меня есть альтернативы? Я не вижу способа имитировать объект Request для теста. И даже если бы я это сделал, это означало бы настройку результатов / точек входа для каждого возможного пути кода, который принимает API Google (который в этом случае нужно было бы найти методом проб и ошибок), но позволил бы мне использовать фикстуры.
Еще одно расширение - это когда определенные запросы полагаются на то, что определенные данные уже находятся в режиме реального времени. Снова используя Google Content API в качестве примера, чтобы добавить поток данных к дополнительной учетной записи, дополнительная учетная запись должна уже существовать.
Один из подходов, который я могу придумать, - это следующие шаги:
testCreateAccount
testCreateDataFeed
в зависимости от testCreateAccount
нет ошибок
testCreateDataFeed
создайте новую учетную запись Тогда возникает следующий вопрос; как проверить удаление аккаунтов / каналов данных? testCreateDataFeed
кажется мне грязным. Что делать, если создать канал данных не удастся? Тест не проходит, поэтому дочерняя учетная запись никогда не удаляется ... Я не могу протестировать удаление без создания, поэтому я должен написать еще один тест ( testDeleteAccount
), который полагается на testCreateAccount
, прежде чем создание и удаление собственной учетной записи (поскольку данные не должны передаваться между тестами).
По теме: