Prueba: DB en memoria vs burla

12

Al escribir pruebas, ¿por qué alguien querría usar una base de datos en memoria en lugar de simplemente burlarse de los datos?

Pude ver que las bases de datos en memoria podrían ser beneficiosas para probar los repositorios de uno. Pero si se utiliza un marco (como Spring Data), probar los repositorios sería probar el marco y no realmente la lógica de la aplicación.

Sin embargo, la burla parece más rápida y sigue el mismo patrón que generalmente se emplea al escribir pruebas unitarias y TDD.

Entonces, ¿qué me estoy perdiendo? ¿Cuándo / por qué sería beneficiosa una base de datos en memoria?


fuente

Respuestas:

14

La burla es la solución ideal para las pruebas unitarias, y también puede usarse para pruebas de integración para mejorar la velocidad, pero no proporciona el mismo nivel de confianza que cuando se usa una base de datos en memoria. Debe escribir pruebas de extremo a extremo donde configure toda la aplicación lo más cerca posible de cómo está configurada la producción y ejecutar pruebas automatizadas en su contra. Estas pruebas deben usar una base de datos real, ya sea en memoria, acoplable, una máquina virtual o alguna otra implementación.

Pero si se utiliza un marco (como Spring Data), probar los repositorios sería probar el marco y no realmente la lógica de la aplicación.

Al usar una base de datos real, está probando que realmente está configurando y usando el marco correctamente. Además, puede haber deficiencias en el marco que solo se revelan cuando se prueba con una base de datos real (ejemplo artificial: Spring Data no admite la versión 9.2 de PostgreSQL).

Escribiría la mayor parte de mi cobertura de prueba contra fuentes simuladas, pero escribiría algunas pruebas de extremo a extremo para casos de uso comúnmente utilizados utilizando una base de datos real.

Samuel
fuente
Si es una prueba unitaria, probaría el marco por separado de la capa que usa el marco. Siempre debe haber algunas pruebas de integración después de realizar todas las pruebas unitarias.
Denise Skidmore
2

La mayoría de las veces, las pruebas de bases de datos en memoria son más simples que las burlas. También es mucho más flexible. Y también prueba que los archivos de migración estén bien (cuando hay archivos de migración).

Vea este pseudocó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]');
    }
}

El InMemoryTestno depende de cómo Databasese implementa en el UserRepositorytrabajo. Simplemente usa la UserRepositoryinterfaz pública ( create) y luego afirma contra ella. Esa prueba no se romperá si cambia la implementación, pero es más lenta.

Mientras tanto, MockingDBTestdepende totalmente de cómo Databasese implementa UserRepository. De hecho, si cambia la implementación pero aún así hace que funcione de otra manera, esa prueba se rompería.

Lo mejor de ambos mundos sería usar una falsa implementación de la Databaseinterfaz:

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;
    }
}

Eso es mucho más expresivo, más fácil de leer y entender, y no depende de la implementación de la base de datos real realizada en las capas superiores del código.

Steve Chamaillard
fuente