Lumen / Laravel:无法弄清楚如何对 json 响应进行单元测试
Lumen / Laravel: Can't figure out how unittesting a json response works
我不知道单元测试是如何工作的。
我有一个控制器returns一个JSON响应
Controller.php
public function getDiscount(Request $request)
{
if (isset($request) && !empty($request)) {
return response()->json($this->discount->calculateDiscount($request->json()->all()))->getOriginalContent();
}
}
使用邮递员,这是这条路线的结果:
Post 客户:
{
"customer-id": "3",
"items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50"
},
{
"product-id": "A102",
"quantity": "1",
"unit-price": "49.50",
"total": "49.50"
}
],
"total": "69.00"
}
API
的回应
{
"applied_discounts": [
{
"id": 3,
"name": "Tools Discount",
"description": "Seems like you really like Tools, here's one for free!"
}
],
"discounted_items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50",
"discounted_price": 15.6
}
],
"discounted_price": 65.1,
"original_price": "69.00"
}
现在,当我尝试进行单元测试时,这就是我想到的:
public function testToolsDiscount()
{
$this->json('POST', '/discount',
[
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
])
->seeJson(
[
'applied_discounts' => [
[
'id' => 3,
]
],
]);
}
然而当我运行它时,这是我得到的错误
DiscountTest::testToolsDiscount Invalid JSON was returned from the
route. Perhaps an exception was thrown?
我做错了什么?
您的 post-body 可能需要是实际的 JSON 字符串,而不是关联数组。 json()
方法也可能需要完全限定的 URL 而不是相对路径。如果是这种情况,这个解决方案实际上可能不会暴露问题,您只需要尝试一下就可以了。否则,试试这个,它至少应该提供一些关于出了什么问题的线索。将以下内容添加到您的单元测试 class 和 dd()
结果中。
/**
* @param string $uri
* @param string $method
* @param array $body
* @param array $headers
* @param array $files
* @param array $cookies
* @return array
*/
public function callRoute(
$uri,
$method = 'GET',
array $body = [],
array $headers = [],
array $files = [],
array $cookies = []
) {
foreach ($cookies as $name => $cookie) {
$this->app->resolving(\App\Http\Middleware\EncryptCookies::class, function (\App\Http\Middleware\EncryptCookies $cookie) use ($name) {
$cookie->disableFor($name);
});
}
$uri = trim($uri, '/');
$uriParts = parse_url($uri);
//This value may be based on the APP_URL value in your .env, I'm not really sure.
$root = !array_get($uriParts, 'host') ? trim(app(\Illuminate\Http\Request::class)->root(), '/').'/' : '';
$uri = "$root$uri";
$server = $this->transformHeadersToServerVars($headers);
$response = $this->call($method, $uri, $body, $cookies, $files, $server);
$headers = $response->headers->all();
$code = $response->getStatusCode();
$json = $content = $response->getContent();
$json = json_decode($json, true);
$content = ($json && json_last_error() == JSON_ERROR_NONE) ? $json : $content;
return compact('code', 'headers', 'content');
}
我敢打赌以下内容会暴露错误消息和堆栈跟踪。您可能必须在目标控制器中使用其他一些 dd()
语句来跟进它,或者在该错误指向正确方向后您的逻辑存在的任何其他地方:
$body = [
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
];
$response = $this->callRoute('POST', '/discount', $body)['content'];
dd($response);
确保您的路由与指定的 '/discount'
及其可能具有的任何前缀相匹配。
定义正确的路线,
我推荐使用action()辅助函数来添加url,这个函数的主要好处是当你改变路由中的一些文本或前缀时,
假设您从 /discount
更改为 /discounts
,在这种情况下您不需要更改所有地方的路线。
action('ControllerName@actionName');
我不知道单元测试是如何工作的。
我有一个控制器returns一个JSON响应
Controller.php
public function getDiscount(Request $request)
{
if (isset($request) && !empty($request)) {
return response()->json($this->discount->calculateDiscount($request->json()->all()))->getOriginalContent();
}
}
使用邮递员,这是这条路线的结果:
Post 客户:
{
"customer-id": "3",
"items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50"
},
{
"product-id": "A102",
"quantity": "1",
"unit-price": "49.50",
"total": "49.50"
}
],
"total": "69.00"
}
API
的回应{
"applied_discounts": [
{
"id": 3,
"name": "Tools Discount",
"description": "Seems like you really like Tools, here's one for free!"
}
],
"discounted_items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50",
"discounted_price": 15.6
}
],
"discounted_price": 65.1,
"original_price": "69.00"
}
现在,当我尝试进行单元测试时,这就是我想到的:
public function testToolsDiscount()
{
$this->json('POST', '/discount',
[
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
])
->seeJson(
[
'applied_discounts' => [
[
'id' => 3,
]
],
]);
}
然而当我运行它时,这是我得到的错误
DiscountTest::testToolsDiscount Invalid JSON was returned from the route. Perhaps an exception was thrown?
我做错了什么?
您的 post-body 可能需要是实际的 JSON 字符串,而不是关联数组。 json()
方法也可能需要完全限定的 URL 而不是相对路径。如果是这种情况,这个解决方案实际上可能不会暴露问题,您只需要尝试一下就可以了。否则,试试这个,它至少应该提供一些关于出了什么问题的线索。将以下内容添加到您的单元测试 class 和 dd()
结果中。
/**
* @param string $uri
* @param string $method
* @param array $body
* @param array $headers
* @param array $files
* @param array $cookies
* @return array
*/
public function callRoute(
$uri,
$method = 'GET',
array $body = [],
array $headers = [],
array $files = [],
array $cookies = []
) {
foreach ($cookies as $name => $cookie) {
$this->app->resolving(\App\Http\Middleware\EncryptCookies::class, function (\App\Http\Middleware\EncryptCookies $cookie) use ($name) {
$cookie->disableFor($name);
});
}
$uri = trim($uri, '/');
$uriParts = parse_url($uri);
//This value may be based on the APP_URL value in your .env, I'm not really sure.
$root = !array_get($uriParts, 'host') ? trim(app(\Illuminate\Http\Request::class)->root(), '/').'/' : '';
$uri = "$root$uri";
$server = $this->transformHeadersToServerVars($headers);
$response = $this->call($method, $uri, $body, $cookies, $files, $server);
$headers = $response->headers->all();
$code = $response->getStatusCode();
$json = $content = $response->getContent();
$json = json_decode($json, true);
$content = ($json && json_last_error() == JSON_ERROR_NONE) ? $json : $content;
return compact('code', 'headers', 'content');
}
我敢打赌以下内容会暴露错误消息和堆栈跟踪。您可能必须在目标控制器中使用其他一些 dd()
语句来跟进它,或者在该错误指向正确方向后您的逻辑存在的任何其他地方:
$body = [
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
];
$response = $this->callRoute('POST', '/discount', $body)['content'];
dd($response);
确保您的路由与指定的 '/discount'
及其可能具有的任何前缀相匹配。
定义正确的路线,
我推荐使用action()辅助函数来添加url,这个函数的主要好处是当你改变路由中的一些文本或前缀时,
假设您从 /discount
更改为 /discounts
,在这种情况下您不需要更改所有地方的路线。
action('ControllerName@actionName');