Teste - banco de dados na memória vs zombaria

12

Ao escrever testes, por que alguém iria querer usar um banco de dados na memória apenas zombando dos dados?

Pude ver que os bancos de dados na memória podem ser benéficos para testar os repositórios de alguém. Mas se utilizando uma estrutura (como o Spring Data), testar os repositórios estaria testando a estrutura e não realmente a lógica do aplicativo.

A zombaria, no entanto, parece mais rápida e segue o mesmo padrão geralmente empregado ao escrever testes de unidade e TDD.

Então o que estou perdendo? Quando / por que um banco de dados na memória seria benéfico?


fonte

Respostas:

14

A zombaria é a solução ideal para testes de unidade e também pode ser usada para testes de integração para melhorar a velocidade, mas não fornece o mesmo nível de confiança que quando você usa um banco de dados na memória. Você deve escrever testes ponto a ponto em que configura o aplicativo inteiro o mais próximo possível de como ele está configurado na produção e executa testes automatizados nele. Esses testes devem usar um banco de dados real - na memória, janela de encaixe, uma VM ou outra implantação.

Mas se utilizando uma estrutura (como o Spring Data), testar os repositórios estaria testando a estrutura e não realmente a lógica do aplicativo.

Ao usar um banco de dados real, você está testando se está realmente configurando e usando a estrutura corretamente. Além disso, pode haver falhas na estrutura que são reveladas apenas ao testar com um banco de dados real (exemplo artificial: o Spring Data não suporta a versão 9.2 do PostgreSQL).

Escrevia a maior parte da minha cobertura de teste contra fontes falsas, mas escrevia alguns testes de ponta a ponta para casos de uso comumente exercitados usando um banco de dados real.

Samuel
fonte
Se for um teste de unidade, você testaria a estrutura separadamente da camada que usa a estrutura. Sempre deve haver alguns testes de integração após a conclusão de todos os testes de unidade.
Denise Skidmore
2

Na maioria das vezes, o teste de banco de dados na memória é mais simples do que zombar. Também é muito mais flexível. E também testa se os arquivos de migração são bem executados (quando há arquivos de migração).

Veja este pseudo-código:

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

        $userRepository = new UserRepository(new Database());
        $userRepository->create('name', '[email protected]');

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

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 protected]']]);

        $userRepository = new UserRepository($databaseMock);
        $userRepository->create('name', '[email protected]');
    }
}

O InMemoryTestnão depende de como Databaseé implementado no UserRepositorytrabalho. Ele simplesmente usa a UserRepositoryinterface pública ( create) e, em seguida, afirma contra ela. Esse teste não será interrompido se você alterar a implementação, mas for mais lento.

Enquanto isso, ele MockingDBTestdepende totalmente de como Databaseé implementado UserRepository. De fato, se você alterar a implementação, mas ainda fazê-la funcionar de outra maneira, esse teste será interrompido.

O melhor dos dois mundos seria usar uma falsa implementação da Databaseinterface:

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

        $this->assertEquals('name', $fakeDatabase->datas['users']['name']);
        $this->assertEquals('[email protected]', $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;
    }
}

Isso é muito mais expressivo, mais fácil de ler e entender, e não depende da implementação do banco de dados real, feito nas camadas superiores do código.

Steve Chamaillard
fonte