Различные методы тестирования
Сначала определите, что вы делаете: модульное тестирование или интеграционное тестирование . Количество слоев не имеет значения для модульного тестирования, поскольку вы, скорее всего, тестируете только один класс. Остальное ты издеваешься. Для тестирования интеграции неизбежно, что вы тестируете несколько слоев. Если у вас есть хорошие модульные тесты, задача состоит в том, чтобы сделать интеграционные тесты не слишком сложными.
Если ваши юнит-тесты хороши, вам не нужно повторять тестирование всех деталей при проведении интеграционного тестирования.
Термины, которые мы используем, немного зависят от платформы, но вы можете найти их практически на всех платформах тестирования / разработки:
Пример приложения
В зависимости от технологии, которую вы используете, имена могут отличаться, но я буду использовать это в качестве примера:
Если у вас есть простое приложение CRUD с моделью Product, ProductsController и представлением индекса, которое генерирует таблицу HTML с продуктами:
Конечным результатом приложения является отображение таблицы HTML со списком всех активных продуктов.
Модульное тестирование
модель
Модель вы можете протестировать довольно легко. Есть разные методы для этого; мы используем светильники. Я думаю, что это то, что вы называете "поддельные наборы данных". Поэтому перед каждым тестом мы создаем таблицу и вводим исходные данные. У большинства платформ есть методы для этого. Например, в вашем тестовом классе - метод setUp (), который запускается перед каждым тестом.
Затем мы запускаем наш тест, например: testGetAllActive products.
Таким образом, мы тестируем непосредственно в тестовой базе данных. Мы не копируем источник данных; мы делаем это всегда одинаково. Это позволяет нам, например, протестировать новую версию базы данных, и возникнут любые проблемы с запросами.
В реальном мире вы не можете всегда следовать 100% единоличной ответственности . Если вы хотите сделать это еще лучше, вы можете использовать источник данных, который вы издеваетесь. Для нас (мы используем ORM) это похоже на тестирование уже существующих технологий. Также тесты становятся намного более сложными, и они на самом деле не тестируют запросы. Так что мы продолжаем в том же духе.
Жестко закодированные данные отдельно хранятся в приборах. Таким образом, фикстура похожа на файл SQL с оператором create table и вставками для используемых нами записей. Мы оставляем их небольшими, если только нет реальной необходимости проводить тестирование с большим количеством записей.
class ProductModel {
public function getAllActive() {
return $this->find('all', array('conditions' => array('active' => 1)));
}
}
контроллер
Контроллеру нужно больше работать, потому что мы не хотим тестировать модель с ним. Итак, что мы делаем, это издеваемся над моделью. Это означает: мы тестируем метод index (), который должен возвращать список записей.
Таким образом, мы смоделируем метод модели getAllActive () и добавим в него фиксированные данные (например, две записи). Теперь мы проверяем данные, которые контроллер отправляет в представление, и сравниваем, действительно ли мы получаем эти две записи обратно.
function testProductIndexLoggedIn() {
$this->setLoggedIn();
$this->ProductsController->mock('ProductModel', 'index', function(return array(your records) ));
$result=$this->ProductsController->index();
$this->assertEquals(2, count($result['products']));
}
Достаточно. Мы пытаемся добавить как можно меньше функций в контроллер, потому что это затрудняет тестирование. Но, конечно, в нем всегда есть какой-то код. Например, мы тестируем требования, такие как: Показать эти две записи, только если вы вошли в систему.
Таким образом, контроллеру обычно требуется один макет и небольшой фрагмент жестко закодированных данных. Для системы входа в систему может быть другой. В нашем тесте у нас есть вспомогательный метод для этого: setLoggedIn (). Это упрощает тестирование с логином или без логина.
class ProductsController {
public function index() {
if($this->loggedIn()) {
$this->set('products', $this->ProductModel->getAllActive());
}
}
}
Взгляды
Тестирование просмотров сложно. Сначала мы выделяем логику, которая повторяется. Мы помещаем это в Помощники и проверяем те классы строго. Мы ожидаем, что всегда один и тот же результат. Например, generateHtmlTableFromArray ().
Тогда у нас есть некоторые конкретные взгляды проекта. Мы не проверяем их. Это не очень желательно для модульного тестирования тех. Мы оставляем их для интеграционных тестов. Поскольку мы взяли много кода в представления, у нас здесь меньший риск.
Если вы начинаете тестировать их, вам, вероятно, придется менять свои тесты каждый раз, когда вы меняете фрагмент HTML, который бесполезен для большинства проектов.
echo $this->tableHelper->generateHtmlTableFromArray($products);
Интеграционное тестирование
Здесь, в зависимости от вашей платформы, вы можете работать с историями пользователей и т. Д. Это может быть веб-интерфейс, например, Selenium или другие аналогичные решения.
Обычно мы просто загружаем базу данных с приборами и утверждаем, какие данные должны быть доступны. Для полного интеграционного тестирования мы обычно используем глобальные требования. Итак: установите продукт активным, а затем проверьте, доступен ли продукт.
Мы не проверяем все снова, например, доступны ли нужные поля. Мы проверяем большие требования здесь. Так как мы не хотим дублировать наши тесты с контроллера или представления. Если что-то действительно является ключевой / основной частью вашего приложения или по соображениям безопасности (проверьте, что пароль недоступен), то мы добавляем их, чтобы убедиться, что это правильно.
Жестко закодированные данные хранятся в приборах.
function testIntegrationProductIndexLoggedIn() {
$this->setLoggedIn();
$result=$this->request('products/index');
$expected='<table';
$this->assertContains($expected, $result);
// Some content from the fixture record
$expected='<td>Product 1 name</td>';
$this->assertContains($expected, $result);
}