How to load routes based on application/json header in Laravel

laravel, php, phpunit, unit-testing

Solution

In my case, I was using `Content-Type` to determine which controllers to load. This didn't work for me, because routes are loaded into memory when `TestCase->createApplication()` is run. This means my headers had no effect.

I ended up making a `RouteInflector` that allows me to force my tests to use the Api routes.

class ApiTestCase extends TestCase
{

    /**
     * @inheritDoc
     */
    public static function setUpBeforeClass()
    {
        /**
         * Routes are loaded into memory before tests are run.
         * Because of this, we can't have routing logic based on
         * heads.  Using the RouteInflector we can override
         * header to createApplication() and must use a constant
         * to force the RouteInflector to use Api controllers.
         */
        RouteInflector::isJson(true);
    }

    public function setUp()
    {
        parent::setUp();

        //Lets do this right
        $this->client->setServerParameter('HTTP_CONTENT_TYPE', 'application/json');
        $this->client->setServerParameter('HTTP_ACCEPT', 'application/json');
    }

}

Inflector:

class RouteInflector
{

    /** @var bool */
    protected static $isJson = false;

    /**
     * Review the request details and determine which controller
     * subpackage should be used.

     * We could also check the request source to help determine the
     * package.
     *
     * Defaults to Web.
     *
     * @return string
     */
    public function getControllerSubpackage()
    {
        if (self::isJson() || Request::isJson()) {
            return 'Api';
        }

        return 'Web';
    }

    /**
     * Used by tests to tell routing that the current request
     * is a json request.
     *
     * @see \Tests\ApiTestCase
     *
     * @param bool|null $isJson
     *
     * @return bool Only provided if parameter is null
     */
    public static function isJson($isJson = null)
    {
        if (is_null($isJson)) {
            return self::$isJson;
        } else {
            self::$isJson = $isJson;
        }
    }
}

Problem

I'm using the `application/json` header to control how my controller acts when a request is received. I need for the POST in my unit test to include an `application/json` header. I've tried: ``` public function testStore() { $this->validator ->shouldReceive('validate') ->once() ->with($this->attributes) ->andReturn(true); $this->repository ->shouldReceive('create') ->once() ->with($this->attributes) ->andReturn($this->entity); $this->controller ->shouldReceive('creationSucceeded') ->once() ->with($this->entity); $this->client->request('POST', 'shared/users', [], [], [ 'HTTP_CONTENT_TYPE' => 'application/json' ], json_encode($this->attributes)); $this->assertResponseStatus(201); } ``` And it the `Request::isJson()` in my controller continues to return false. I also tried using `'CONTENT_TYPE' => 'application/json'` instead of the `HTTP_CONTENT_TYPE` above.

Original source

Related problems