在编写测试时,为什么有人要使用内存数据库来模拟数据呢?
我可以看到内存数据库对于测试一个人的存储库可能是有益的。但是,如果使用框架(例如Spring Data),则测试存储库将测试框架,而不是测试应用程序逻辑。
但是,模拟似乎更快,并且遵循编写单元测试和TDD时通常采用的相同模式。
那我想念什么呢?何时/为什么内存数据库会受益?
在编写测试时,为什么有人要使用内存数据库来模拟数据呢?
我可以看到内存数据库对于测试一个人的存储库可能是有益的。但是,如果使用框架(例如Spring Data),则测试存储库将测试框架,而不是测试应用程序逻辑。
但是,模拟似乎更快,并且遵循编写单元测试和TDD时通常采用的相同模式。
那我想念什么呢?何时/为什么内存数据库会受益?
Answers:
模拟是单元测试的理想解决方案,它也可以用于集成测试以提高速度,但是它不能提供与使用内存数据库时相同的置信度。您应该编写端到端测试,在其中配置整个应用程序,使其与生产配置方式尽可能接近,并对其进行自动测试。这些测试应使用真实的数据库-内存,泊坞窗,VM或其他一些部署。
但是,如果使用框架(例如Spring Data),则测试存储库将测试框架,而不是测试应用程序逻辑。
通过使用真实的数据库,您正在测试自己是否实际上在配置和正确使用框架。此外,该框架中可能存在一些缺陷,只有在使用实际数据库进行测试时才能发现这些缺陷(伪造的示例:Spring Data不支持PostgreSQL的9.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;
}
}
这种方式更具表现力,更易于阅读和理解,并且不依赖于在更高代码层中完成的实际数据库的实现。