Nathan Clow Nathan Clow - 4 months ago 21
PHP Question

Post bodies ignored when making multiple post calls in laravel test

I've been running into problems writing my phpunit tests in lumen5.2 with laravel components. If I make multiple http calls to my API within a single test,the body I supply for subsequent calls is ignored in favor of the first body supplied to any http call in the test. The problem occurs using any of the available methods in MakesHttpRequests, such as post() or put() or call(). The problem is similar but not identical to issues discussed here and here, but their solutions are not applicable or don't fix my issue. I've distilled it down to the following behavior:

EchoTest.php



<?php

class EchoTest extends TestCase
{
public function testEcho()
{
$this->json('POST', '/echo', ['string' => "first"]);
$this->json('POST', '/echo', ['string' => "second"]);
$this->json('POST', '/echo', ['string' => "third"]);
}
}


EchoController.php



<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Input;

class EchoController extends Controller
{
public function _echo()
{
$input = Input::json()->all();
var_dump($input['string']);
}
}


routes.php



<?php

$app->post('echo', ['uses' => 'EchoController@_echo']);


Output



.string(5) "first"
string(5) "first"
string(5) "first"


I've found that calling
$this->refreshApplication()
after each post call helps somewhat, but also breaks the DatabaseTransactions logic, leaving the database littered with test data that contaminates subsequent test runs, and also having off-by-one side effects like not fixing the problem for the last post before the refresh.

I'm rather stumped on what I'm doing wrong here. I've traced the request handling down several layers until I lose it in all the container magic underneath and can't find any obvious bugs there.

Answer

After a great deal of trial and error, I found that calling Facade::clearResolvedInstances() after every http call makes successive http calls work properly. This seems to avoid the side-effects of breaking database transactions that come from refreshApplication. I ended up wrapping all the http verb methods to automatically call the method in this manner:

public function get($uri, array $headers = [])
{
    $ret = parent::get($uri, $headers);
    Facade::clearResolvedInstances();
    return $ret;
}

I still have no idea why such a thing is necessary.