yii2单元测试中应使用yii\test\request并用yii::$app->set('request', $mock)注册,而非直接赋值;需显式设置url、method、bodyparams、rawbody、servervars等属性,并手动添加cookie对象,以准确模拟真实请求行为。

Yii2 中怎么用 createRequest() 伪造 HTTP 请求对象
单元测试里不能真发 HTTP 请求,得让 Yii::$app->request 返回你可控的实例。Yii2 提供了 yii\test\Request,但它不是开箱即用的“模拟器”,而是个可配置的假请求类,必须手动挂载到应用组件里。
常见错误是直接 new 一个 Request 然后赋值给 Yii::$app->request —— 这会破坏组件生命周期,导致 getIsGet()、getBodyParams() 等方法行为异常,甚至触发 InvalidConfigException。
- 必须通过
Yii::$app->set('request', $mockRequest)注册,而不是直接赋值 -
yii\test\Request的$url、$method、$bodyParams等属性需在构造后显式设置,否则默认为空或 null - 如果测试中依赖
getRemoteIP()或getUserAgent(),记得设置$serverVars数组(如['REMOTE_ADDR' => '127.0.0.1'])
$request = new \yii\test\Request();
$request->url = '/api/users';
$request->method = 'POST';
$request->bodyParams = ['name' => 'test'];
$request->serverVars = ['REQUEST_METHOD' => 'POST'];
Yii::$app->set('request', $request);
为什么不能用 Mockery 或 PHPUnit::createMock() 直接 mock Request 类
因为 Yii 的 Request 是深度耦合组件:它读取 $_SERVER、$_GET、$_POST,还依赖 Yii::$app->getSecurity() 解密 cookie。纯 mock 会绕过这些逻辑,导致 getCookies() 返回空、getCsrfToken() 报错,或者 resolvePathInfo() 解析失败。
更实际的问题是:很多控制器动作里会调用 $this->request->get() 或 $this->request->post(),而这些方法内部做了参数过滤和类型转换——mock 对象无法复现这一层行为。
- mock 出的
get()方法返回的是原始数组,不会自动调用filterInput() - mock 不会响应
enableCsrfValidation = false配置,CSRF 校验仍可能意外触发 - 若测试涉及文件上传(
getUploadedFiles()),mock 几乎无法还原$_FILES结构和临时路径逻辑
yii\test\Request 和真实 yii\web\Request 的关键差异点
它们共享接口,但行为有三处必须注意:
-
getAbsoluteUrl()在 test\Request 中只拼接http://localhost+$url,不读$_SERVER['HTTP_HOST'],所以带端口或 HTTPS 的场景会出错 -
getHostInfo()默认返回'http://localhost',无法通过配置覆盖,硬编码写死 -
getCookies()不解析$_COOKIE,而是直接返回$cookies属性(CookieCollection实例),需手动 add 一个Cookie对象才能生效
如果你的代码里写了 $request->getCookies()->getValue('X-TOKEN'),就得这么配:
$cookie = new \yii\web\Cookie(['name' => 'X-TOKEN', 'value' => 'abc123']); $request->cookies->add($cookie);
测试 POST JSON 请求时,getRawBody() 和 getJson() 怎么设
Yii 默认不自动解析 JSON body,要靠 getJson() 内部调用 json_decode($this->getRawBody(), true)。所以伪造时不能只设 $bodyParams,还得设 $rawBody 字符串。
-
$bodyParams是给get()/post()用的,不影响getJson() -
$rawBody必须是合法 JSON 字符串,且不能带 BOM 或多余空格,否则getJson()返回 null - 如果控制器用了
Yii::$app->request->getJson(),但你只设了bodyParams,结果永远是 null
$request = new \yii\test\Request();
$request->method = 'POST';
$request->rawBody = '{"name":"test","age":25}';
$request->serverVars = ['CONTENT_TYPE' => 'application/json'];
最常被忽略的是 CONTENT_TYPE 没设对,或者 rawBody 是 PHP 数组而非 JSON 字符串——这两个点一错,getJson() 就静默失效,debug 时容易以为是代码问题,其实是测试准备没到位。











