测试-内存中数据库与模拟


12

在编写测试时,为什么有人要使用内存数据库来模拟数据呢?

我可以看到内存数据库对于测试一个人的存储库可能是有益的。但是,如果使用框架(例如Spring Data),则测试存储库将测试框架,而不是测试应用程序逻辑。

但是,模拟似乎更快,并且遵循编写单元测试和TDD时通常采用的相同模式。

那我想念什么呢?何时/为什么内存数据库会受益?

Answers:


14

模拟是单元测试的理想解决方案,它也可以用于集成测试以提高速度,但是它不能提供与使用内存数据库时相同的置信度。您应该编写端到端测试,在其中配置整个应用程序,使其与生产配置方式尽可能接近,并对其进行自动测试。这些测试应使用真实的数据库-内存,泊坞窗,VM或其他一些部署。

但是,如果使用框架(例如Spring Data),则测试存储库将测试框架,而不是测试应用程序逻辑。

通过使用真实的数据库,您正在测试自己是否实际上在配置和正确使用框架。此外,该框架中可能存在一些缺陷,只有在使用实际数据库进行测试时才能发现这些缺陷(伪造的示例:Spring Data不支持PostgreSQL的9.2版)。

我会针对模拟源编写大部分测试内容,但会使用真实数据库针对常用的用例编写一些端到端测试。


如果是单元测试,则将框架与使用框架的层分开进行测试。在完成所有单元测试之后,总应该有一些集成测试。
丹尼斯·斯基德莫尔

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

这种方式更具表现力,更易于阅读和理解,并且不依赖于在更高代码层中完成的实际数据库的实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.