Pengujian - In-Memory DB vs Mocking

12

Saat menulis tes, mengapa seseorang ingin menggunakan basis data dalam memori hanya dengan mengejek data?

Saya dapat melihat bahwa basis data dalam memori dapat bermanfaat untuk menguji repositori seseorang. Tetapi jika menggunakan kerangka kerja (seperti Spring Data), menguji repositori akan menguji kerangka kerja dan tidak benar-benar logika aplikasi.

Namun, mengejek tampaknya lebih cepat dan mengikuti pola yang sama yang biasanya digunakan saat menulis unit test dan TDD.

Jadi apa yang saya lewatkan? Kapan / mengapa basis data dalam memori akan bermanfaat?


sumber

Jawaban:

14

Mengejek adalah solusi ideal untuk pengujian unit, dan ini juga dapat digunakan untuk pengujian integrasi untuk meningkatkan kecepatan, tetapi itu tidak memberikan tingkat kepercayaan yang sama seperti ketika Anda menggunakan basis data dalam memori. Anda harus menulis tes ujung ke ujung di mana Anda mengkonfigurasi seluruh aplikasi sedekat mungkin dengan cara mengkonfigurasi produksi dan menjalankan tes otomatis terhadapnya. Tes-tes ini harus menggunakan database nyata - baik dalam memori, buruh pelabuhan, VM, atau penyebaran lainnya.

Tetapi jika menggunakan kerangka kerja (seperti Spring Data), menguji repositori akan menguji kerangka kerja dan tidak benar-benar logika aplikasi.

Dengan menggunakan database nyata Anda menguji bahwa Anda sebenarnya mengkonfigurasi dan menggunakan kerangka kerja dengan benar. Lebih lanjut mungkin ada kekurangan dalam kerangka kerja yang hanya terungkap saat pengujian dengan database aktual (contoh dibuat: Spring Data tidak mendukung versi 9.2 dari PostgreSQL).

Saya akan menulis sebagian besar cakupan pengujian saya terhadap sumber-sumber yang diejek, tetapi saya akan menulis beberapa tes end-to-end untuk kasus penggunaan yang biasa dilakukan menggunakan database nyata.

Samuel
sumber
Jika ini adalah tes Unit, Anda akan menguji kerangka kerja secara terpisah dari lapisan yang menggunakan kerangka kerja. Harus selalu ada beberapa tes integrasi setelah semua pengujian unit dilakukan.
Denise Skidmore
2

Sebagian besar waktu, pengujian basis data dalam memori lebih sederhana daripada mengejek. Ini juga jauh lebih fleksibel. Dan itu juga menguji file migrasi dilakukan dengan baik (ketika ada file migrasi).

Lihat kode semu ini:

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

Tidak InMemoryTesttergantung pada bagaimana Databasediterapkan ke dalam UserRepositorypekerjaan. Itu hanya menggunakan UserRepositoryantarmuka publik ( create) dan kemudian menegaskan menentangnya. Tes itu tidak akan rusak jika Anda mengubah implementasi tetapi lebih lambat.

Sementara itu, MockingDBTestsepenuhnya bergantung pada bagaimanaDatabase diimplementasikan ke dalam UserRepository. Bahkan, jika Anda mengubah implementasi tetapi masih membuatnya bekerja dengan cara lain, tes itu akan pecah.

Terbaik dari kedua dunia akan menggunakan palsu yang mengimplementasikan Databaseantarmuka:

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

Itu jauh lebih ekspresif, lebih mudah dibaca dan dimengerti, dan itu tidak tergantung pada implementasi dari Database aktual yang dilakukan pada lapisan kode yang lebih tinggi.

Steve Chamaillard
sumber