bez*_*nez 2 php unit-testing laravel laravel-5 laravel-middleware
在我开始之前,我想说我已经看过Failed断言HTTP状态是200而不是500以及Failed断言HTTP状态代码是200而不是500.不要让这些名字欺骗你; 他们是两个不同的职位.这两个帖子都超过9个月,没有好的答案.
我在测试路线方面遇到了一些麻烦.所以,我Laravel 5在Mac Mini上运行了一个新项目OS X Yosemite 10.10.2.我正在制作一个API.所以,我开始首先制作API的后端部分.我决定用PHPUnit这个项目来实现TDD.我的版本PHPUnit是4.0.20.我正在PHP 5.5.14和我使用的是打包服务器Laravel的artisan命令行工具.
现在,我开始确保我可以连接到服务器.所以,我的第一个测试看起来像:
public function testGetMessagesResponse()
{
    $response = $this->call('GET', 'Messages');
    $this->assertEquals(200, $response->getStatusCode());
}
这传递得很好.但是,当我转到POST请求时,它失败了.这是我的第二次测试:
public function testPostMessagesResponse()
{
    $response = $this->call('POST', 'Messages');
    $this->assertEquals(200, $response->getStatusCode());
}
此测试失败.这是输出的样子:
1) MessagesTest::testPostMessagesResponse
Failed asserting that 500 matches expected 200.
在我的routes.php文件中,我确实注册了此路由:
Route::resource('Messages', 'MessagesController');
在我的MessagesController中,我有所有必需的方法:
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class MessagesController extends Controller {
    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        return 'response';
    }
    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        return 'response';
    }
    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store()
    {
        return 'response';
    }
    /**
    * Display the specified resource.
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        return 'response';
    }
    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return Response
     */
    public function edit($id)
    {
        return 'response';
    }
    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        return 'response';
    }
    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function destroy($id)
    {
        return 'response';
    }
}
所以,暂时,无论HTTP动词,我都应该得到'response'.现在,我决定用curl验证测试:
curl -X POST http://myapp.com/Messages
这让Laravel'哎呀!' 页.确实存在某种服务器错误,但我不确定该寻找什么.应用程序流程看起来不错,但显然不起作用.这是我的Laravel日志中的堆栈跟踪:
[timestamp] local.ERROR: exception 'Illuminate\Session\TokenMismatchException' in /Users/username/Sites/project/storage/framework/compiled.php:2375
Stack trace:
#0 /Users/username/Sites/project/app/Http/Middleware/VerifyCsrfToken.php(17): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#1 /Users/username/Sites/project/storage/framework/compiled.php(8858): App\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#2 /Users/username/Sites/project/storage/framework/compiled.php(11990): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#3 /Users/username/Sites/project/storage/framework/compiled.php(8858): Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#4 /Users/username/Sites/project/storage/framework/compiled.php(10696): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#5 /Users/username/Sites/project/storage/framework/compiled.php(8858): Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#6 /Users/username/Sites/project/storage/framework/compiled.php(11696): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#7 /Users/username/Sites/project/storage/framework/compiled.php(8858): Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#8 /Users/username/Sites/project/storage/framework/compiled.php(11645): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#9 /Users/username/Sites/project/storage/framework/compiled.php(8858): Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#10 /Users/username/Sites/project/storage/framework/compiled.php(2411): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#11 /Users/username/Sites/project/storage/framework/compiled.php(8858): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#12 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#13 /Users/username/Sites/project/storage/framework/compiled.php(8849): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#14 /Users/username/Sites/project/storage/framework/compiled.php(1862): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#15 /Users/username/Sites/project/storage/framework/compiled.php(1852): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#16 /Users/username/Sites/project/public/index.php(53): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#17 /Users/username/Sites/project/server.php(21): require_once('/Users/username/Site...')
#18 {main}
我对Laravel很新,因此我不确定如何真正深入挖掘并使用堆栈跟踪来找出问题所在.我也没试过这个Route::resource,但这并没有缓解我的情况.我的GET仍然可以正常工作(Route::get),但我的POST(Route::post)仍然得到相同的500错误.包含....../compiled.php中的第2375行的函数(来自堆栈跟踪顶部抛出的异常)由Laravel生成,如下所示:
public function handle($request, Closure $next)
{
    if ($this->isReading($request) || $this->tokensMatch($request)) {
        return $this->addCookieToResponse($request, $next($request));
    }
    throw new TokenMismatchException();
}
那时,我试图编辑POST测试,因为我实际上并没有发送任何数据.所以这是我修改的第二次测试:
public function testPostMessagesResponse()
{
    $response = $this->call('POST', 'Messages', array("key" => "value"));
    $this->assertEquals(200, $response->getStatusCode());
}
我收到相同的错误消息.我觉得我错过了一些简单的东西,但我无法理解.任何帮助将不胜感激.
@Alan Storm:你对正在发生的事情完全正确.我在Laracasts上发布了同样的问题,你所描述的问题在那里得到了反映.然而,为了给予信用到期的信用,我收到了更优雅的问题解决方案.所以,我在这里重新发布它,以便其他人拥有它,如果他们有同样的问题.来自Laracasts的JarekTkaczyk:
问题是,Laravel 5为每个请求运行VerifyCsrfToken中间件,没有例外.它验证请求是读取(GET,HEAD,OPTIONS)还是具有有效令牌.
在您的情况下,没有有效的令牌,因此您获得TokenMismatchException.现在,要解决此问题,您可以禁用此验证以用于测试环境.
只需将app/Http/Middleware/VerifyCsrfToken.php调整为:
public function handle($request, Closure $next)
{
    if ('testing' !== app()->environment())
    {
        return parent::handle($request, $next);
    }
    return $next($request);
}
现在,快乐测试!
您可以在调用堆栈的第一个函数中看到您的问题
#0 /Users/username/Sites/project/app/Http/Middleware/VerifyCsrfToken.php(17): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
看起来Laravel 5会自动检查CSFR令牌的POST请求(跨站点请求伪造).这将是您在基于浏览器的UI中在表单上创建的内容,以帮助防止跨站点请求伪造.
由于A CSFR令牌对API没有意义,因此您需要删除此中间件.看起来Laravel 5应用程序的默认设置为您提供了一个Kernal类文件
#File: app/Http/Kernel.php
<?php namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel {
    /**
     * The application's global HTTP middleware stack.
     *
     * @var array
     */
    protected $middleware = [
        'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
        'Illuminate\Cookie\Middleware\EncryptCookies',
        'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
        'Illuminate\Session\Middleware\StartSession',
        'Illuminate\View\Middleware\ShareErrorsFromSession',
        'App\Http\Middleware\VerifyCsrfToken',
    ];
    /**
     * The application's route middleware.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => 'App\Http\Middleware\Authenticate',
        'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
        'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
    ];
}    
对于您的API项目,您可以删除
'App\Http\Middleware\VerifyCsrfToken',
从$middlewares酒店入口,你会很高兴.如果您要使用浏览器UI创建应用程序,则需要保留此中间件,然后阅读中间件以了解如何在特定请求中使用它.(简短版本?中间件是一种在每个HTTP请求上执行但不影响实际应用程序代码的方法)
| 归档时间: | 
 | 
| 查看次数: | 3760 次 | 
| 最近记录: |