PHP Slim 3 Unit Testing

Recently I have been fielding a lot of questions about how to mock PSR-7 objects to unit test Slim. A lot of people have found it difficult because of the complexity of the constructors of the Slim HTTP classes. I can't say I blame them, it's something that the core team is working on improving. The HTTP classes are heavily coupled to the Environment object.

Mocking

For those unfamiliar Mocking means constructing an object with dummy data to produce an object that can be used for testing.

Here is a slim example for a Request Factory. For a lot of use cases this function is perfect for most of your needs.

    public function requestFactory()
    {
        $env = Environment::mock();
        $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123');
        $headers = Headers::createFromEnvironment($env);
        $cookies = [
            'user' => 'john',
            'id' => '123',
        ];
        $serverParams = $env->all();
        $body = new RequestBody();
        $uploadedFiles = UploadedFile::createFromEnvironment($env);
        $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);
        return $request;
    }

Let's take a look at a few key things you need to modify in order to get this to work for you.

  1. Uri - This should point to the route that you want to test. For the purposes of the tests the example.com doesn't actually matter. It should be something like http://example.com/your/route, its pretty straight forward. You replicate the actual real world route in this line
  2. Cookies - I don't ever use them, I do not see a need for them so this is always blank for me.
  3. Request Body - This is where things get a pick tricky.
    1. This should be a string
    2. You should always rewind after you are done constructing the body
    3. It should be encoded in the same format as THE CONTENT-TYPE for the request.
  4. Content-Type - This is where most people have difficulties, please pay attention to this point.
    1. When your application receives a request, it is basically a giant String. PHP parses the headers for you automatically, it does not always parse the BODY of the request. The only special circumstance where it does this, is when it is handling normally encoded form data (multipart/form-data). PHP does NOT understand application/json, or text/xml...nothing.
    2. When using JSON or any other encoding format, check to make sure Slim supports it. *hint* Slim supports JSON and XML given the correct Content-Type header is set.
    3. PLEASE SET THE CORRECT CONTENT-TYPE HEADER!

These 4 things should help you in your efforts in Unit testing Requests.

Execution of Tests

You should be using the following syntax to execute your app to test. In the following $resOut will be a ResponseInterface object

        // Invoke app
        $resOut = $app($req, $res);

I hope this helps you in your goal to Unit test your Slim App!

Example

//Mock Test with Content Type

$request = $this->requestFactory();

$request->write(json_encode([ 'hello' => 'world' ]));
$request->getBody()->rewind();

$request = $request->withHeader('Content-Type', 'application/json');
$request = $request->withMethod('POST');

$app = new App();
$path = '/foo';
$callable = function ($req, $res) {
    return $res->write($req->getParsedBody['hello']);
};

$app->get($path, $callable);

$resOut = $app($request, new Response());
$resOut->getBody()->rewind();

$this->assertEquals('world', $resOut->getBody()->getContents());

Happy Coding o7

Written by Glenn Eggleton on Thursday March 3, 2016
Permalink - Chapter: Slim