web-dev-qa-db-ja.com

Laravel PHPUnitモックリクエスト

コントローラーでPHPUnitを実行していますが、リクエストを正しくモックできないようです。

これがコントローラーです:

_use Illuminate\Http\Request;

public function insert(Request $request)
{
    // ... some codes here
    if ($request->has('username')) {
        $userEmail = $request->get('username');
    } else if ($request->has('email')) {
        $userEmail = $request->get('email');
    }
    // ... some codes here
}
_

次に、単体テストで、

_public function testIndex()
{
    // ... some codes here

    $requestParams = [
        'username' => 'test',
        'email'    => '[email protected]'
    ];

    $request = $this->getMockBuilder('Illuminate\Http\Request')
        ->disableOriginalConstructor()
        ->setMethods(['getMethod', 'retrieveItem', 'getRealMethod', 'all', 'getInputSource', 'get', 'has'])
        ->getMock();

    $request->expects($this->any())
        ->method('get')
        ->willReturn($requestParams);

    $request->expects($this->any())
        ->method('has')
        ->willReturn($requestParams);

    $request->expects($this->any())
        ->method('all')
        ->willReturn($requestParams);

    // ... some codes here
}
_

ここでの問題は、私がvar_dump($request->has('username');するときはいつでも、配列全体である_$requestParams_値を常に返すことです。ユーザー名キーが配列に存在するため、trueが返されるはずです。

次に、_$requestParams_のユーザー名キーを削除すると、配列にusernameキーが含まれていないため、falseが返されるはずです。

5
basagabi

私が見る限り、あなたはユニットテストに、リクエストオブジェクトで$ request-> has()を呼び出すと、trueやfalseなどではなく$ requestParams配列を返す必要があることを伝えています。

メソッド呼び出しで送信内容を具体的に確認しない限り、モックは実際に送信内容を気にせず、呼び出されたことだけを気にします。

ユースケースで可能であれば、空のリクエストを作成してデータを入力することを検討することをお勧めします。これにより、単体テストをより簡単に、より少ない問題で実行できるようになります。これはすべての場合に機能するとは限りません。

ユニットテストにどのアサーションを作成しているかを含めると、発生している内容をより明確に確認できますが、現状のままです。それはあなたが返すように言っていることを正確に返します。それがあなたが実際にそれを返したいものでなくても。

モックは、ユニットテストをシステムの他の部分から分離するために使用されます。そのため、通常、特定のメソッドが呼び出されたかどうかをチェックして、コードが実際にモックしたクラスに存在するかどうか、および送信する予定のデータが含まれているかどうかを確認する傾向があります。極端な場合には、実際にテストしているシステムをモックしたい場合がありますが、これは通常、コードが他のクラスに依存しすぎているか、実行しすぎていることを示しています。

モックを使用するもう1つの理由は、メソッド呼び出しで型キャストの制約を満たすためです。このような場合、通常は空のモックオブジェクトを作成し、コードが受け入れるか、コードをテストするために中断するダミーデータをそのオブジェクトに入力します。

あなたの場合、コードが実際に正しく機能するかどうかを確認したいようです。そのためには、リクエストをモックしないか、trueまたはfalseを返すように指示する特定のテストを行うことをお勧めします(両方の場合のテスト)

したがって、次のようなものがあります。

$request->expects($this->any())
    ->method('has')
    ->with('username')
    ->willReturn(true); // or false in your next test

編集:以下のコメントで述べたように、コードでhasメソッドを複数回使用しているという問題が発生し、問題が発生しました。

応答コメントでリンクした質問について詳しく説明しますが、要約すると、インライン関数またはat()メソッドを使用して複数のケースを処理できます。

At()を使用すると、コードの特定の反復を指定して、テストのそのビットのみをヒットできます。これにより、前のテストがテストを中断する前に追加されたテストがかなり脆弱になることが言及されています。

$request->expects($this->at(0))
    ->method('has')
    ->with('username')
    ->willReturn('returnValue');

$request->expects($this->at(1))
    ->method('has')
    ->with('email')
    ->willReturn('otherReturnValue');

インライン関数(コールバック)ソリューションを使用すると、テストをカスタマイズして複数のケースを許可し、必要に応じてデータを返すことができます。残念ながら、私はこれまで自分で使用したことがないため、この概念にあまり精通していません。詳細については、 PHPUnit docs を読むことをお勧めします。

結局、リクエストをモックするのではなく、チェックしたいデータを入力する空のリクエストを作成することをお勧めします。 Laravelには、通常テストする多くのデータを手動でリクエストに入力できる、いくつかの印象的なメソッドが付属しています。

たとえば、を使用してデータを追加(データの投稿/取得)できます

request->add(['fieldname' => 'value'])

最後のいくつかのポインタとして、var_dumpを使用しているように思われることを述べておきたいと思います。 Laravelには、類似していてデバッグに非常に役立つ2つの独自の関数が付属しています。dd();またはdump();dd();ダンプを使用できます。コードの実行を停止し、dump();は決定したものを出力するだけなので、dd($request);またはdump($request);を実行して、変数/クラスオブジェクトなどが何を保持しているかを確認できます。何が入っているかなどを確認できるように、Javascriptなどを使用してかなり洗練されたレイアウトにすることもできます。存在することを知らなかった場合は、チェックしてみてください。

3
Tropus

リクエストをモックするのは理想的ではありませんが、とにかくやりたい場合があります。

protected function createRequest(
    $method,
    $content,
    $uri = '/test',
    $server = ['CONTENT_TYPE' => 'application/json'],
    $parameters = [],
    $cookies = [],
    $files = []
) {
    $request = new \Illuminate\Http\Request;
    return $request->createFromBase(
        \Symfony\Component\HttpFoundation\Request::create(
            $uri,
            $method,
            $parameters,
            $cookies,
            $files,
            $server,
            $content
        )
    );
}
10
Ian