Laravel 工厂由于嵌套关系而减慢测试速度
Laravel factories slowing down testing due to nested relationships
对于上下文,我正在研究基于 RNG 的赛车模拟器,因为没有更好的术语。用户可以创建宇宙(想想现实生活中的 FIA),在宇宙中他们可以创建系列(想想 F1、F2、F3 等),并且在每个系列中他们可以创建季节。在其中的每一个中,用户都可以创建其他模型;
在 Universe 中,用户可以创建团队和车手。
在一个赛季中,用户可以使用他们在任何宇宙之外添加的电路创建日历,ent运行ts(基于他们在父宇宙中创建的团队)和这些 ent运行他们可以添加驱动程序(基于创建的驱动程序),我称之为“阵容”。
我对这些关系的测试越深入,我需要通过工厂创建的模型越多才能正确测试,而测试到 运行 所需的时间也越长。我有一个非常简单的测试来验证宇宙所有者可以将驱动程序添加到属于该宇宙的 ent运行t;
test('a universe owner can add drivers to entrants', function () {
$user = User::factory()->create();
$season = createSeasonForUser($user);
$driver = Driver::factory()->for($season->universe)->create();
$entrant = Entrant::factory()->for($season)->create();
$this->actingAs($user)
->post(route('seasons.lineups.store', [$season]), [
'driver_id' => $driver->id,
'entrant_id' => $entrant->id,
'number' => 2,
])
->assertRedirect(route('seasons.lineups.index', [$season]));
$this->assertDatabaseCount('lineups', 1);
$this->assertCount(1, $entrant->drivers);
$this->assertCount(1, $season->drivers);
});
我有两个辅助函数可以快速创建属于特定用户的系列 and/or 季;
function createSeriesForUser(User $user): Series
{
return Series::factory()->for(Universe::factory()->for($user)->create())->create();
}
function createSeasonForUser(User $user): Season
{
$series = createSeriesForUser($user);
return Season::factory()->for($series)->create();
}
如您所见,为了测试流程的一部分,我需要通过工厂创建六个模型(其中一些工厂有时会调用更多工厂)。我 运行 测试了五次,对测试的每个部分(工厂、实际请求和断言)进行计时,工厂平均占用 1.9 秒,其余测试占用 0.015秒,这对我来说似乎不对。
理想情况下,我会在每个测试文件 运行 之前创建所有必需的数据库条目,但我知道这是不好的做法。还有其他方法可以加快测试速度吗?遗憾的是,我无法减少关系的嵌套或复杂性,因为这些只是我正在构建的网站的要求。
或者,这种方法通常是否是测试我的控制器功能的正确方法,或者可以采用不同的方法吗?
为了不把问题弄得过于混乱,here's一个包含所有当前工厂的 pastebin
事实证明 Faker 的 image()
真的 很慢。我用 'avatar' => null,
替换了我的 UserFactory 中的 'avatar' => $this->faker->image(),
,现在我的整个测试服 运行 只用了不到三秒,如果我 运行 它们并行,则只需一秒钟。
对于上下文,我正在研究基于 RNG 的赛车模拟器,因为没有更好的术语。用户可以创建宇宙(想想现实生活中的 FIA),在宇宙中他们可以创建系列(想想 F1、F2、F3 等),并且在每个系列中他们可以创建季节。在其中的每一个中,用户都可以创建其他模型;
在 Universe 中,用户可以创建团队和车手。
在一个赛季中,用户可以使用他们在任何宇宙之外添加的电路创建日历,ent运行ts(基于他们在父宇宙中创建的团队)和这些 ent运行他们可以添加驱动程序(基于创建的驱动程序),我称之为“阵容”。
我对这些关系的测试越深入,我需要通过工厂创建的模型越多才能正确测试,而测试到 运行 所需的时间也越长。我有一个非常简单的测试来验证宇宙所有者可以将驱动程序添加到属于该宇宙的 ent运行t;
test('a universe owner can add drivers to entrants', function () {
$user = User::factory()->create();
$season = createSeasonForUser($user);
$driver = Driver::factory()->for($season->universe)->create();
$entrant = Entrant::factory()->for($season)->create();
$this->actingAs($user)
->post(route('seasons.lineups.store', [$season]), [
'driver_id' => $driver->id,
'entrant_id' => $entrant->id,
'number' => 2,
])
->assertRedirect(route('seasons.lineups.index', [$season]));
$this->assertDatabaseCount('lineups', 1);
$this->assertCount(1, $entrant->drivers);
$this->assertCount(1, $season->drivers);
});
我有两个辅助函数可以快速创建属于特定用户的系列 and/or 季;
function createSeriesForUser(User $user): Series
{
return Series::factory()->for(Universe::factory()->for($user)->create())->create();
}
function createSeasonForUser(User $user): Season
{
$series = createSeriesForUser($user);
return Season::factory()->for($series)->create();
}
如您所见,为了测试流程的一部分,我需要通过工厂创建六个模型(其中一些工厂有时会调用更多工厂)。我 运行 测试了五次,对测试的每个部分(工厂、实际请求和断言)进行计时,工厂平均占用 1.9 秒,其余测试占用 0.015秒,这对我来说似乎不对。
理想情况下,我会在每个测试文件 运行 之前创建所有必需的数据库条目,但我知道这是不好的做法。还有其他方法可以加快测试速度吗?遗憾的是,我无法减少关系的嵌套或复杂性,因为这些只是我正在构建的网站的要求。
或者,这种方法通常是否是测试我的控制器功能的正确方法,或者可以采用不同的方法吗?
为了不把问题弄得过于混乱,here's一个包含所有当前工厂的 pastebin
事实证明 Faker 的 image()
真的 很慢。我用 'avatar' => null,
替换了我的 UserFactory 中的 'avatar' => $this->faker->image(),
,现在我的整个测试服 运行 只用了不到三秒,如果我 运行 它们并行,则只需一秒钟。