Codeception测试Yii2时如何处理预期抛出的UserException
Yii2 Codeception功能测试预期业务异常被误判为错误的解决方法
问题场景
在Yii2框架下编写功能测试用例,校验site/verify-email接口传入空token的场景,测试代码如下:
public function checkEmptyToken2(FunctionalTester $I) { $I->amOnRoute('site/verify-email', ['token' => '']); $I->canSee('Email verify token did not come through for some reason'); }
运行测试时,代码抛出了预期内的yii\base\UserException,但Codeception直接将该异常判定为测试错误,执行到请求步骤就终止,没有走到后续的canSee文本校验断言。尝试过用try/catch捕获异常、调用expectException声明预期异常两种方案,都没有解决问题,测试依然报错终止。
报错根因
Codeception的Yii2模块默认会拦截请求处理流程中抛出的所有未捕获异常,直接标记为测试错误,不会把异常交给Yii应用的错误处理器去渲染业务错误页。代码里抛出的UserException没有在控制器层捕获处理,请求流程直接中断,后续的页面断言自然不会执行。
可行解决方案
方案1:修改测试模块配置(推荐,无需改动业务代码)
编辑功能测试套件的配置文件,一般路径为tests/functional.suite.yml,在Yii2模块配置中添加异常过滤规则,让指定的业务异常走应用本身的错误渲染流程,不中断测试:
modules: enabled: - Yii2: configFile: 'config/test.php' # 新增以下配置 errorHandler: exceptionFilter: class: yii\web\ExceptionFilter exceptionMap: yii\base\UserException: yii\web\BadRequestHttpException
配置修改完成后,执行命令重新生成测试actor代码:
php vendor/bin/codecept build
配置生效后再运行测试,遇到UserException时不会直接中断,会正常渲染错误提示页,后续的canSee断言可以正常执行校验。
方案2:调整业务代码异常处理逻辑
如果不想修改测试配置,可以在控制器层捕获业务异常,手动渲染错误提示页后返回响应,不要把异常直接抛到应用入口:
// SiteController的actionVerifyEmail方法中调整逻辑 public function actionVerifyEmail($token = '') { try { $model = new VerifyEmailForm(['token' => $token]); // 原有业务逻辑 } catch (\yii\base\UserException $e) { // 传入错误提示,渲染自定义错误页 return $this->render('verify-email-fail', ['errorMsg' => $e->getMessage()]); } }
这种场景下请求流程不会抛出未捕获异常,测试会正常加载页面内容,canSee断言可正常工作。
之前方案失效的原因
- try/catch包裹
$I->amOnRoute()无效:异常是在Yii2模块内部的请求分发流程中抛出的,不是在测试方法的当前代码块直接抛出,外层try/catch无法捕获模块内部的异常。 expectException无效:该方法是PHPUnit提供的、用于断言测试执行过程中直接抛出指定异常即算测试通过的方法,和需要的「异常抛出后继续校验页面渲染内容」的需求完全不匹配。
内容的提问来源于stack exchange,提问作者Senthil
相关产品推荐
相关产品推荐

