Как выполнить интеграционное тестирование с помощью dataProvider не создавая кучу экземпляров модели?
Хочу протестировать методы приложения. При этом это инеграционное тестирование, то есть прямая связь с БД. Например пользователей нужно проверить на "активность", на "роль/права", на "состоит ли в друзьях" и другие всякие методы. Для этого будет много разных методов тестирования. Но для каждого из них нужно много входных данных, провайдеры даннных. Сейчас я пишу что-то типа такого:
<?php
namespace tests\unit\services\user;
use app\models\User;
use app\services\User as UserService;
use Codeception\Test\Unit;
class UserTest extends Unit {
/**
* @var \UnitTester
*/
protected $tester;
protected function _before() { }
protected function _after() { }
/**
* @dataProvider getUsersForActivity
*/
public function testUserIsActive($user, $result) {
$userService = new UserService($user);
self::assertEquals($userService->isActive(), true);
}
public function getUsersForActivity() {
return [
[User::findOne(['id' => 1]) => true],
[User::findOne(['id' => 2]) => false],
[User::findOne(['id' => 456]) => false],
[User::findOne(['id' => 87]) => true],
// куча других пользователей
];
}
}
И вот таких провайдеров может быть десяток. В каждом из них модель обращается к БД, тянет данные и тестируется. Это слишком трудозатрадно для БД, учитывая, что это только пользователи, а могут быть ещё другие сотни тестов, которые ещё больше трудоёмкие.
Вопрос: как правильно делать интеграционное тестирование? Как правильно загрузить данные для прогона теста, чтобы это не загружать так БД? Например как-то создать кэшированный запрос и подставлять туда id из массива?
Ответы (1 шт):
Интеграционные тесты медленные, поэтому:
- их нужно использовать только для того, что нельзя протестировать в unit тестах.
- идти на определенные компромиссы
В примере, который вы привели, нет смысла тестировать активность для такого количества пользователей, достаточно протестировать для одного активного и второго неактивного. Любые два активных пользователя с точки зрения этой функциональности эквивалентны.
Важно понимать и четко себе ответить, что тестирует тест. Действительно ли интеграцию с БД. Или все же другую логику, которую можно протестировать в unit тесте, замокав вызов к БД. Это позволит существенно сократить количество тестов, которым действительно нужна БД.
Имеет смысл для тестирования использовать in-memory БД. Насколько я понимаю, то sqlite можно использовать в php в этом режиме. Настройте себе, чтобы при запуске интеграционных тестов использовалась именно inmemory БД.
Имеет смысл укрупнять тесты. То есть вместо тестирования конкретного метода, типа User.findOne тестируйте какой-то сценарий, в котором этот метод будет использован. Теряется независимость тестов и увеличивается гранулярность, поэтому нужно взвешивать, что важнее.
Что касается создания данных. Статические данные (типа словарей) имеет смысл создать заранее, и использовать уже готовую БД, чтоб во время выполнения тестов, не нужно было все вставлять по-новой.
Не знаю поддерживает ли система запуска тестов это, но если да, то нужно использовать распараллеливание тестов. Для этого важно чтоб они были независимыми в том смысле, что не использовали разделяемых данных. Часто есть возможность изолировать по пользователю, т.е. каждый тест использует своего пользователя в системе, большинство других данных привязано к пользователю, и тесты меньше друг с другом конфликтуют.