Тестирование - БД в памяти против Mocking

12

При написании тестов, почему кто-то хотел бы использовать базу данных в памяти, а не просто издеваться над данными?

Я мог видеть, что базы данных в памяти могут быть полезны для тестирования своих репозиториев. Но если использовать каркас (такой как Spring Data), тестирование репозиториев будет проверять каркас, а не логику приложения.

Однако издевательство кажется более быстрым и следует той же схеме, которая обычно используется при написании модульных тестов и TDD.

Так чего мне не хватает? Когда / почему будет полезна база данных в памяти?


источник

Ответы:

14

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

Но если использовать каркас (такой как Spring Data), тестирование репозиториев будет проверять каркас, а не логику приложения.

Используя реальную базу данных, вы проверяете, что на самом деле вы настраиваете и правильно используете инфраструктуру. Кроме того, в структуре могут быть недостатки, которые обнаруживаются только при тестировании с реальной базой данных (надуманный пример: Spring Data не поддерживает версию 9.2 PostgreSQL).

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

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

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

Посмотрите этот псевдокод:

class InMemoryTest 
{
    /** @test */
    public function user_repository_can_create_a_user()
    {
        $this->flushDatabase();

        $userRepository = new UserRepository(new Database());
        $userRepository->create('name', 'email@email.com');

        $this->seeInDatabase('users', ['name' => 'name', 'email' => 'email@email.com']);
    }
}

class MockingDBTest
{
    /** @test */
    public function user_repository_can_create_a_user()
    {
        $databaseMock = MockLib::mock(Database::class);
        $databaseMock->shouldReceive('save')
                     ->once()
                     ->withArgs(['users', ['name' => 'name', 'email' => 'email@email.com']]);

        $userRepository = new UserRepository($databaseMock);
        $userRepository->create('name', 'email@email.com');
    }
}

InMemoryTestНе зависит от того, как Databaseреализуется в UserRepositoryк работе. Он просто использует UserRepositoryоткрытый интерфейс ( create), а затем утверждает против него. Этот тест не сломается, если вы измените реализацию, но он будет медленнее.

Между тем, MockingDBTestполностью полагается на то, как Databaseреализовано в UserRepository. На самом деле, если вы измените реализацию, но все равно заставите ее работать по-другому, этот тест будет сорван.

Лучшее из обоих миров - использовать фальшивую реализацию Databaseинтерфейса:

class UsingAFakeDatabaseTest
{
    /** @test */
    public function user_repository_can_create_a_user()
    {
        $fakeDatabase = new FakeDatabase();
        $userRepository = new UserRepository($fakeDatabase);
        $userRepository->create('name', 'email@email.com');

        $this->assertEquals('name', $fakeDatabase->datas['users']['name']);
        $this->assertEquals('email@email.com', $fakeDatabase->datas['users']['email']);
    }
}

interface DatabaseInterface
{
    public function save(string $table, array $datas);
}

class FakeDatabase implements DatabaseInterface
{
    public $datas;

    public function save(string $table, array $datas)
    {
        $this->datas[$table][] = $datas;
    }
}

Это намного более выразительно, легче для чтения и понимания, и это не зависит от реализации реальной базы данных, выполненной на более высоких уровнях кода.

Стив Шамайяр
источник