2

始める前に、HTTP ステータスが 200 ではなく 500であると主張する Failed と、HTTP ステータス コードが 200 ではなく 500 であると主張する Failedをすでに確認したことをお伝えしたいと思います。名前にだまされてはいけません。それらは2つの異なる投稿です。これらの投稿はどちらも 9 か月以上前のもので、適切な回答がありません。

ルートのテストに問題があります。そこで、Laravel 5Mac Mini で新しいプロジェクトを作成しましたOS X Yosemite 10.10.2。APIを作っています。そこで、まず API のバックエンド部分を作り始めました。PHPUnitこのプロジェクトの TDD を実装するために使用することにしました。の私のバージョンはPHPUnitです4.0.20。私は実行中で、コマンド ライン ツールでPHP 5.5.14パッケージ化されたサーバーを使用しています。Laravelartisan

さて、サーバーに接続できることを確認することから始めました。したがって、私の最初のテストは次のようになります。

public function testGetMessagesResponse()
{
    $response = $this->call('GET', 'Messages');

    $this->assertEquals(200, $response->getStatusCode());
}

これは問題なく通過します。しかし、POST リクエストに移ると失敗しました。これは私の2番目のテストがどのように見えるかです:

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動詞に関係なく、 get を取得する必要があります'response'。ここで、curl を使用してテストを検証することにしました。

curl -X POST http://myapp.com/Messages

これにより、Laravel 'Oops!' が返されます。ページ。確かにある種のサーバーエラーがありますが、何を探すべきかわかりません。アプリケーション フローは良さそうに見えますが、明らかに機能していません。私の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 テストを編集しようとしました。だからここに私の修正された2番目のテストがあります:

public function testPostMessagesResponse()
{
    $response = $this->call('POST', 'Messages', array("key" => "value"));

    $this->assertEquals(200, $response->getStatusCode());
}

同じエラー メッセージが表示されます。シンプルなものが欠けているような気がしますが、理解できません。どんな助けでも大歓迎です。

4

4 に答える 4

6

@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);
}

それでは、テストをお楽しみください。

于 2015-02-06T15:27:58.977 に答える
4

現在ここにある 2 つの代替ソリューションを提供したいと思います。どちらのソリューションも、CSRF ミドルウェアを回避しています (無効にするか、その背後にあるコードを変更することにより)。CSRF保護が必要でテストしたい正当なケースでは、代表的でないテストに対応するようにコードを変更するよりも、テストを変更する方が良いと思います.

また、これに似たコードブロックを持つと思います

if ('testing' !== app()->environment()) {
   // Path taken only when not testing
} 

@beznez's own answerのように、コードのどこでも、卑劣なバグを求めています。可能な限り、テスト コードは、コードを操作するすべての人と同じパスを取る必要があります。

通常のブラウザー リクエストに存在するのと同じように、実際にリクエストに CSRF トークンを含めることはそれほど難しくありません。唯一の秘訣は、セッションを開始してから、(フォームと同様に)_tokenによって返される値を使用してリクエストにキーを追加する必要があることです。csrf_token()したがって、変更されたテスト コードは次のようになります。

public function testPostMessagesResponse()
{
    Session::start(); // Start a session        

    $response = $this->call(
        'POST', 'Messages', array("key" => "value", "_token" => csrf_token()));

    $this->assertEquals(200, $response->getStatusCode());
}

これについては、このブログ投稿で詳しく説明しています。

于 2015-06-23T07:56:26.183 に答える