← Назад к вопросам

Как протестировать запрос к БД?

2.0 Middle🔥 161 комментариев
#Базы данных и SQL#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Стратегии тестирования запросов к базе данных

Тестирование запросов к БД — критически важная часть разработки backend-приложений на PHP. Я использую многоуровневый подход, который охватывает различные аспекты работы с базой данных.

Изоляция тестов от реальной БД

Первое и самое важное правило: юнит-тесты не должны зависеть от реальной базы данных. Для этого применяю:

// Использование репозиториев с зависимостями через интерфейсы
interface UserRepositoryInterface {
    public function findById(int $id): ?User;
    public function save(User $user): void;
}

class UserService {
    public function __construct(
        private UserRepositoryInterface $repository
    ) {}
    
    public function activateUser(int $userId): bool {
        $user = $this->repository->findById($userId);
        if (!$user) {
            return false;
        }
        $user->activate();
        $this->repository->save($user);
        return true;
    }
}

Тестирование SQL-запросов

Для проверки корректности самих SQL-запросов использую:

  1. Моки репозиториев в юнит-тестах
  2. Тестирование Query Builder через сравнение с ожидаемым SQL
  3. Интеграционные тесты с тестовой БД
// Пример интеграционного теста с использованием PHPUnit и тестовой БД
class UserRepositoryTest extends TestCase {
    private PDO $connection;
    
    protected function setUp(): void {
        $this->connection = new PDO('sqlite::memory:');
        $this->createTestSchema();
    }
    
    public function testFindByEmailReturnsCorrectUser(): void {
        // Arrange
        $repository = new UserRepository($this->connection);
        $testEmail = 'test@example.com';
        $this->insertTestUser($testEmail);
        
        // Act
        $user = $repository->findByEmail($testEmail);
        
        // Assert
        $this->assertNotNull($user);
        $this->assertEquals($testEmail, $user->getEmail());
    }
}

Использование фикстур и миграций

Для подготовки тестового окружения:

  • Миграции БД для создания структуры таблиц
  • Сиды (Seeders) для заполнения тестовыми данными
  • Транзакции для изоляции каждого теста
// Пример теста с использованием транзакций
class TransactionalTest extends TestCase {
    use DatabaseTransactions;
    
    public function testComplexQuery(): void {
        // Данные автоматически откатываются после теста
        $user = User::create(['name' => 'Test User']);
        
        $result = DB::select('SELECT * FROM users WHERE id = ?', [$user->id]);
        
        $this->assertCount(1, $result);
    }
}

Тестирование производительности запросов

Для проверки эффективности запросов:

-- Анализ EXPLAIN запроса в тестах
EXPLAIN SELECT * FROM users 
WHERE email = 'test@example.com' 
AND status = 'active';

Инструменты и библиотеки

В моем стеке для тестирования БД:

  • PHPUnit — основной фреймворк для тестирования
  • Doctrine DBAL — для абстракции работы с БД
  • Laravel Database Testing (если использую Laravel)
  • DBUnit — для расширенного тестирования БД
  • SQLite in-memory — для быстрых интеграционных тестов

Тестирование ORM-запросов

При работе с ORM (Eloquent, Doctrine):

// Тестирование Eloquent scope
class UserTest extends TestCase {
    public function testActiveScope(): void {
        User::factory()->create(['active' => true]);
        User::factory()->create(['active' => false]);
        
        $activeUsers = User::active()->get();
        
        $this->assertCount(1, $activeUsers);
        $this->assertTrue($activeUsers->first()->active);
    }
}

Ключевые проверки в тестах

При тестировании запросов обязательно проверяю:

  • Корректность возвращаемых данных
  • Обработку граничных случаев (пустые результаты, NULL значения)
  • Пагинацию и сортировку
  • Оптимизацию запросов (N+1 problem, индексы)
  • Транзакционность при нескольких операциях
  • Обработку ошибок и исключений

Моки и стабы для сложных сценариев

Для тестирования сложных бизнес-процессов:

// Мок соединения с БД
$mockConnection = $this->createMock(PDO::class);
$mockStatement = $this->createMock(PDOStatement::class);

$mockStatement->method('fetch')
    ->willReturn(['id' => 1, 'name' => 'Test']);
    
$mockConnection->method('prepare')
    ->willReturn($mockStatement);
    
$repository = new UserRepository($mockConnection);
$result = $repository->findById(1);

$this->assertEquals('Test', $result->getName());

Непрерывная интеграция

В CI/CD пайплайнах:

  • Запуск миграций перед тестами
  • Использование Docker-контейнеров с тестовой БД
  • Параллельное выполнение тестов для ускорения
  • Анализ покрытия кода SQL-запросами

Заключение: Полноценное тестирование запросов к БД требует комбинации юнит-тестов (с моками), интеграционных тестов (с тестовой БД) и проверки производительности. Главный принцип — изоляция тестов друг от друга и от продакшн-окружения, что достигается через транзакции, in-memory базы данных и правильное проектирование слоя доступа к данным.

Как протестировать запрос к БД? | PrepBro