laravel 测试网络路由

laravel testing web routes

我有制造商模块的网络路由(处理资源的支持路由)

Route::resource('/admin/manufactures', App\Http\Controllers\Back\ManufacturerController::class);

我已经创建了一个带有简单规则名称的 ManufacturerRequest=> 必需

并且我想使用 Laravel 测试来测试资源 (CRUD)

在我的控制器中,存储方法如下

public function store(ManufacturerRequest $request)
{
        //db
        $request->validate();

        Manufacturer::create($request->all());
 }

我有一个简单的测试

$response = $this->post('/admin/manufactures' ,
[
   '_token' => csrf_token(),
   'name'   => 'test'
]);
       

$response->assertStatus(200);

这是 return 403,但除此之外,存储方法采用处理验证的 ManufacturerRequest 对象,在测试的情况下,我通过了一个数组,因为它只接受数组。

那么我如何创建一个“模拟”表单的测试并将请求传递给控制器​​以测试验证和 CRUD

你想做的事情很简单,Documentation 上也有解释,完整阅读文档非常重要,这样你就可以大致了解你可以用框架做什么,特别是因为你是新手。

由于您没有指定您使用的是哪个版本,我将使用Laravel 8,但总体上大致相同。

根据您的代码:

资源路由

Route::resource('/admin/manufactures', ManufacturerController::class);

控制器

public function store(ManufacturerRequest $request)
{
    //db
    $request->validate();

    Manufacturer::create($request->all());
}

您需要将控制器更改为:

public function store(ManufacturerRequest $request)
{
    //db

    Manufacturer::create($request->all());
}

是的,只需删除 $request->validate();,因为框架会自动解析 FormRequestauthorize and validate。如果您阅读了部分 validate 解释,您将看到:

So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic.

所以,当控制器的第一行是运行时,表示FormRequest通过了授权检查并验证了输入。

您还可以在控制器上更新的是:

public function store(ManufacturerRequest $request)
{
    //db

    Manufacturer::create($request->validated());
}

看到我已经将 $request->all() 更改为 $request->validated()validated 将仅 return 您在 FormRequest 的 rules 上具有键的字段,如果您使用 all 您将传递请求中的所有内容(还传递 non-validated 数据,这并不好)。

在你尝试任何事情之前,我建议你阅读 关于这个 post,这样你可以更清楚地了解测试。

所以,您收到 403 可能是因为您有一个中间件要求您登录,而您没有使用 $this->actingAs()


因为你没有分享FormRequest规则,我就举一个超小的例子。如果你里面有这条规则:

'name' => ['required', 'string'],

你可以做的测试是:

public function test_manufacturer_is_created(): void
{
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->post('/admin/manufactures', ['name' => $name = 'Manufacturer 1']);

    $response->assertSuccessful();

    $this->assertDatabaseHas(
        'manufacturers',
        [
            'name' => $name
        ]
    );
}

/**
 * @depends test_manufacturer_is_created
 */
public function test_unauthorized_error_is_thrown_when_the_user_is_not_logged_in(): void
{
    $response = $this->post('/admin/manufactures', ['name' => 'Manufacturer 1']);

    $response->assertUnauthorized();
}

/**
 * @depends test_manufacturer_is_created
 * @dataProvider invalidDataProvider
 */
public function test_error_should_be_returned_when_invalid_data_is_sent($value, bool $deleteField): void
{
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->post(
            '/admin/manufactures',
            ! $deleteField ? ['name' => $value] : []
        );

    $response->assertInvalid(['name']);
}

public function invalidDataProvider(): array
{
    return [
        'Missing name' => [null, true],
        'Empty name' => ['', false],
        'Null name' => [null, false],
        'Array name' => [[], false],
        'Number name' => [123, false],
        'Boolean name' => [true, false],
    ];
}

记住我在这里用了很多东西:

  • 我已经测试了正常插入是否有效,是否正在检查是否输入了有效名称(FormRequest 规则),如果用户未登录,它应该抛出未经授权的异常。
  • 我使用了 @depends,仅当相关测试通过时才用于 运行 测试,这样我们就可以防止 运行 进行“负面”测试,因为正常流程没有成功,所以 运行 其他的也没有意义,也得到“测试未通过”。
  • 我还使用了 @dataProvider,它用于与测试共享数据,所以不用 copy-pasting 测试很多次数据变化,你只需改变你想要的数据要使用的测试。