You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中PUT更新资源时断言测试返回500而非预期200的问题

为何Laravel更新Todo的测试返回500状态码?

我是PHP新手,正在开发一个CRUD待办事项应用。借助Laravel的便捷性开发过程很顺利,但遇到了一个问题。

尝试通过以下端点更新"todo"数组:

Route::put("/todos/{user}/{id}", [TodoController::class, "update"])->middleware(["auth", "verified"])->name("todo.update");

该端点接收请求中的$user和$id参数,由update控制器处理:

public function update(Request $request, $user, $id)
{
    $todo = Todo::findOrFail($id);

    if ($user->id != $todo->user_id) {
        return response()->json([
            "error" => $todo->errors()->first(),
        ], 400);
    }

    $validator = Validator::make($request->all(), [
        "text" => ["required", "string"],
        "user_id" => ["required", "integer", "string"],
        "isDone" => ["required", "integer", "boolean", "string"],
    ]);

    if ($validator->fails()) {
        return response()->json([
            "error" => $validator->errors()->first(),
        ], 400);
    }

    $requestData = $request->only(["text", "isDone"]);

    return response()->json(Todo::edit($id, $requestData), 200);
}

最终调用模型的edit方法:

public static function edit($id, $data)
{
    return self::where("id", $id)->update($data);
}

编写了TodoControllerTest.php来测试控制器,测试代码如下:

<?php

namespace Tests\Feature;

use App\Models\User;
use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;


class TodoControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_authenticated_user_can_update_todo()
    {
        $user = User::factory()->create();
        $todo = Todo::factory()->create(['user_id' => $user->id]);

        $updatedData = [
            "text" => "updated todo text",
            "isDone" => true
        ];

        $response = $this->actingAs($user)->put(route('todo.update', ['user' => $user->id, 'id' => $todo->id]), $updatedData);

        $response->assertStatus(200);
        $this->assertDatabaseHas("todos", $updatedData);
    }
}

测试结果如下:

FAILED Tests\Feature\TodoControllerTest > authenticated user can update todo
Expected response status code [200] but received 500.
Failed asserting that 500 is identical to 200.


错误原因分析

  • 路由参数处理错误:路由中{user}参数传入的是用户ID(整数),但控制器方法直接将$user当作User模型实例调用$user->id,此时$user只是普通数值,并非模型对象,直接引发致命错误。
  • 验证规则逻辑冲突:user_id和isDone的验证规则同时包含integer、boolean、string,规则本身矛盾;且测试请求未传入user_id,本应触发验证失败,但因前面的参数错误已经抛出500,验证错误未被返回。
  • 错误处理逻辑无效:$todo->errors()->first()是错误调用,findOrFail返回的模型实例没有errors()方法,该方法属于验证器,此处会抛出方法不存在的错误。

修复方案

1. 修正路由与控制器参数绑定

利用Laravel隐式模型绑定,直接注入模型实例,避免手动处理ID:

// 路由保持不变,Laravel会自动解析参数为模型实例
Route::put("/todos/{user}/{id}", [TodoController::class, "update"])->middleware(["auth", "verified"])->name("todo.update");

// 控制器方法修改为注入User和Todo模型
public function update(Request $request, User $user, Todo $todo)
{
    // 验证当前登录用户是否拥有该待办事项
    if (auth()->id() !== $todo->user_id) {
        return response()->json([
            "error" => "无权修改该待办事项"
        ], 403);
    }

    // 修正验证规则:移除冲突规则,user_id无需前端提交
    $validator = Validator::make($request->all(), [
        "text" => ["required", "string"],
        "isDone" => ["required", "boolean"],
    ]);

    if ($validator->fails()) {
        return response()->json([
            "error" => $validator->errors()->first(),
        ], 400);
    }

    $requestData = $request->only(["text", "isDone"]);
    $todo->update($requestData);

    return response()->json($todo, 200);
}

2. 修正模型edit方法(可选)

如果保留静态edit方法,确保返回更新后的模型而非受影响行数:

public static function edit($id, $data)
{
    $todo = self::findOrFail($id);
    $todo->update($data);
    return $todo;
}

3. 修正测试代码

无需在请求中传入user_id,且断言数据库时需结合todo的ID,避免数据冲突:

public function test_authenticated_user_can_update_todo()
{
    $user = User::factory()->create();
    $todo = Todo::factory()->create(['user_id' => $user->id]);

    $updatedData = [
        "text" => "updated todo text",
        "isDone" => true
    ];

    $response = $this->actingAs($user)->put(route('todo.update', ['user' => $user->id, 'id' => $todo->id]), $updatedData);

    $response->assertStatus(200);
    // 结合ID断言,确保是当前待办事项被更新
    $this->assertDatabaseHas("todos", array_merge($updatedData, ['id' => $todo->id]));
}

内容的提问来源于stack exchange,提问作者Esam Olwan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:24:51