6

I want to test the endpoints of my Slim application with PHPUnit. I'm struggling to mock POST requests, as the request body is always empty.

  • I've tried the approach as described here: Slim Framework endpoint unit testing. (adding the environment variable slim-input)
  • I've tried writing to php://input directly, but I've found out php://input is read only (the hard way)

The emulation of the environment works correctly as for example the REQUEST_URI is always as expected. I've found out that the body of the request is read out in Slim\Http\RequestBody from php://input.

Notes:

  • I want to avoid calling the controller methods directly, so I can test everything, including endpoints.
  • I want to avoid guzzle because it sends an actual request. I do not want to have a server running while testing the application.

my test code so far:

//inherits from Slim/App
$this->app = new SyncApiApp(); 

// write json to //temp, does not work
$tmp_handle = fopen('php://temp', 'w+');
fwrite($tmp_handle, $json);
rewind($tmp_handle);
fclose($tmp_handle);

//override environment
$this->app->container["environment"] =
    Environment::mock(
        [
            'REQUEST_METHOD' => 'POST',
            'REQUEST_URI' => '/1.0/' . $relativeLink,
            'slim.input' => $json,
            'SERVER_NAME' => 'localhost',
            'CONTENT_TYPE' => 'application/json;charset=utf8'
        ]
    );

 //run the application
 $response = $this->app->run();
 //result: the correct endpoint is reached, but $request->getBody() is empty

Whole project (be aware that I've simplified the code on stackoverflow): https://github.com/famoser/SyncApi/blob/master/Famoser.SyncApi.Webpage/tests/Famoser/SyncApi/Tests/

Note 2: I've asked at the slimframework forum, link: http://discourse.slimframework.com/t/mock-slim-endpoint-post-requests-with-phpunit/973. I'll keep both stackoverflow and discourse.slimframework up to date what is happening.

Note 3: There is a currently open pull request of mine for this feature: https://github.com/slimphp/Slim/pull/2086

Florian Moser
  • 2,583
  • 1
  • 30
  • 40
  • 1
    why not just to send POST request using z.b. Guzzle then? – Tebe Nov 29 '16 at 13:44
  • I've changed the title of my question. I want to use PHPUnit to test the endpoints – Florian Moser Nov 29 '16 at 13:45
  • Could you give us an example endpoint and test? I am not sure what your provided code is trying to do. – nerdlyist Nov 29 '16 at 13:57
  • I've added a link to the project, and my test code. I did not specify the endpoint code, as the project is quite large. In my debugging environment however I could verify that `$request->getBody()` is empty – Florian Moser Nov 29 '16 at 15:19
  • I've made post requests from the application which consumes this API, and there $request->getBody() is not empty. – Florian Moser Nov 29 '16 at 15:22

1 Answers1

5

There was help over at http://discourse.slimframework.com/t/mock-slim-endpoint-post-requests-with-phpunit/973/7, the solution was to create the Request from scratch, and write to the request body.

//setup environment vals to create request
$env = Environment::mock();
$uri = Uri::createFromString('/1.0/' . $relativeLink);
$headers = Headers::createFromEnvironment($env);
$cookies = [];
$serverParams = $env->all();
$body = new RequestBody();
$uploadedFiles = UploadedFile::createFromEnvironment($env);
$request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);

//write request data
$request->write(json_encode([ 'key' => 'val' ]));
$request->getBody()->rewind();
//set method & content type
$request = $request->withHeader('Content-Type', 'application/json');
$request = $request->withMethod('POST');

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

$this->assertEquals('full response text', $resOut->getBody()->getContents());

The original blog post which helped to answer was at http://glenneggleton.com/page/slim-unit-testing

Florian Moser
  • 2,583
  • 1
  • 30
  • 40