Laravel测试自定义异常时expectException()断言失败原因
问题背景
涉及的核心代码如下:
- 自定义异常类
namespace App\Exceptions; use Exception; class CustomException extends Exception { public function render($request) { return response()->view('custom-exception'); } }
- 表单请求类中的异常抛出逻辑
class LoginRequest extends FormRequest { public function authenticate() { if (! Auth::attempt($this->only('email', 'password'))) { throw new CustomException(); } } }
- 调用表单请求的控制器代码
class AuthenticatedSessionController extends Controller { public function store(LoginRequest $request) { $request->authenticateMember(); $request->session()->regenerate(); return redirect()->intended(RouteServiceProvider::Home); } }
- 测试代码
use Tests\TestCase; use App\Models\User; use App\Exceptions\CustomException; class EmailVerificationTest extends TestCase { public function test_email_verification_screen_can_be_rendered() { $user = User::factory()->create([ 'email_verified_at' => null, ]); // $this->expectException(CustomException::class); // 该断言无法通过 $response = $this->post( '/login', [ 'email' => 'john@example.com', 'password' => 'secret' ] ); $response->assertViewIs('custom-exception'); $this->assertInstanceOf(CustomException::class, $response->exception); } }
测试现象:
- 以下两个断言可以正常通过:
$response->assertViewIs('custom-exception'); $this->assertInstanceOf(CustomException::class, $response->exception);
$this->expectException(CustomException::class)断言无法通过,报错信息:
Failed asserting that exception of type "App\Exceptions\CustomException" is thrown.
根本原因
- PHPUnit自带的
expectException断言,校验逻辑是检测测试执行流中是否有未被捕获的异常直接冒泡到PHPUnit运行层,只有异常没有被任何外层逻辑接住、直接抛到测试方法执行边界时,这个断言才会判定通过。 - Laravel处理HTTP测试请求时,内置了全局异常拦截机制:请求执行全流程抛出的所有异常,都会先被框架核心的异常处理器(
Illuminate\Foundation\Exceptions\Handler)捕获,不会直接向外冒泡到测试层。- 你为
CustomException定义了render方法,框架捕获到异常后会直接调用该方法,生成返回custom-exception视图的响应,异常到这一步就已经被处理完成,不会继续向外抛出。 - 之所以能通过
$response->exception拿到异常实例、通过assertViewIs验证返回视图,是Laravel在处理异常生成响应时,特意把捕获到的异常实例挂载到了Response对象的exception属性上,方便测试做校验,不代表异常逃逸到了PHPUnit可以检测到的层级。
- 你为
- 顺带提两处代码笔误,虽然不影响当前断言失败的结果,但实际运行会报错:
- 抛出异常时缺少
new关键字,正确写法为throw new CustomException() - 控制器中调用的方法名是
authenticateMember(),但表单请求内定义的验证方法名为authenticate(),方法名不匹配。
- 抛出异常时缺少
内容的提问来源于stack exchange,提问作者Autodesk
相关产品推荐
相关产品推荐

