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

如何让Laravel 9.x的单元测试与功能测试同时通过?

解决Laravel控制器分支逻辑导致单元/功能测试无法同时通过的问题

问题场景

我有两个测试类:Tests\Unit\BlogTest 和 Tests\Feature\BlogTest。在BlogController的Create方法中,当验证失败时,我通过app()->runningUnitTests()做分支判断:单元测试环境返回422响应,非单元环境则重定向到编辑路由并携带错误信息。但出现矛盾:

  • 保留该条件时,单元测试通过,但功能测试失败
  • 移除该条件时,功能测试通过,但单元测试失败

现有代码

BlogController的Create方法片段:

if ($validator->fails()) {
  if (app()->runningUnitTests()) {
    return response('Validation failed', 422);
  }
  return redirect()->route('blog_edit', ['id' => $id])
    ->withErrors($validator)
    ->withInput();
}

Tests\Unit\BlogTest测试方法:

public function test_blog_create_validation()
{
    $reqData = ['title' => null, 'author' => null, 'content' => null];
    $response = $this->postJson(
        action([BlogController::class, 'create']),
        $reqData
    )->assertStatus(422);
}

Tests\Feature\BlogTest测试方法:

public function test_blog_create_validation()
{
    $response = $this->post('/blog', [
        'title'   => '',
        'author'  => '',
        'content' => ''
    ]);
    
    $response->assertSessionHasErrors([
        'title',
        'author',
        'content'
    ]);
}

问题根源

用app()->runningUnitTests()区分响应格式的逻辑不合理:

  • 单元测试中的postJson本质是JSON请求,理应返回422格式的错误响应
  • 功能测试中的普通post是表单提交,理应返回重定向并携带会话错误
  • 但runningUnitTests()会强制所有单元测试环境下的请求返回422,不管请求类型;功能测试若在单元环境运行时,也会被错误触发422分支,导致断言失败

解决方案

把分支判断依据从「是否在单元测试环境」改成「请求是否需要JSON响应」,Laravel提供的$request->wantsJson()方法可以准确判断请求是否期望JSON格式响应(包括postJson发起的请求)。这样就能根据请求类型返回对应响应,与测试类型无关。

修改后的控制器代码

use Illuminate\Http\Request;

// 在Create方法中注入Request实例
public function create(Request $request)
{
    // ... 其他业务代码
    
    if ($validator->fails()) {
        // 根据请求类型返回对应响应
        if ($request->wantsJson()) {
            return response()->json(['errors' => $validator->errors()], 422);
        }
        return redirect()->route('blog_edit', ['id' => $id])
            ->withErrors($validator)
            ->withInput();
    }
    
    // ... 其他业务代码
}

测试验证

  • 单元测试:postJson请求触发wantsJson()为true,返回带错误信息的422 JSON响应,assertStatus(422)可正常通过
  • 功能测试:普通post请求触发重定向分支,错误信息存入会话,assertSessionHasErrors可正常断言

额外优化(可选)

如果需要兼容前端AJAX表单提交场景,可以把判断条件扩展为:

if ($request->wantsJson() || $request->ajax()) {
    // 返回JSON响应
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:57:42