I am attempting to unit test my implementation of a Stripe webhook handler. Stripe webhook data comes across the wire as raw JSON in the body of a POST request, so I capture and decode the data as such:
public function store()
{
$input = @file_get_contents("php://input");
$request = json_decode($input);
return Json::encode($request);
}
I'm attempting to unit test this code, but I can't figure out how to send raw JSON data in a unit test such that I can retrieve it with the file_get_contents("php:input//")
function. This is what I've tried (using PHPUnit
):
protected $testRoute = 'api/stripe/webhook';
protected $validWebhookJson = <<<EOT
{
"id": "ch_14qE2r49NugaZ1RWEgerzmUI",
"object": "charge",
// and a bunch of other fields too
}
EOT;
public function testWebhookDecdoesJsonIntoObject()
{
$response = $this->call('POST', $this->testRoute, $this->validWebhookJson); // fails because `$parameters` must be an array
$response = $this->call('POST', $this->testRoute, [], [], ['CONTENT_TYPE' => 'application/json'], $this->validWebhookJson);
dd($response->getData(true)); // array(0) {} BOOOO!!! Where for to my data go?
}
I've also tried curl
but that would make an external request, which doesn't make sense to me from a unit-testing perspective. How can I simulate a POST request with raw JSON data in the body that will be picked up by my store
method?
You can. But you need to send the encoded JSON as the content (aka request body) and not the parameter.
$this->call(
'POST',
'/articles',
[],
[],
[],
$headers = [
'HTTP_CONTENT_LENGTH' => mb_strlen($payload, '8bit'),
'CONTENT_TYPE' => 'application/json',
'HTTP_ACCEPT' => 'application/json'
],
$json = json_encode(['foo' => 'bar'])
);
That's the 7th parameter.
If you look at the method definition (in Laravel's core) you should be able to see what it expects.
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
This currently isn't supported yet by Laravel 5.1's post, patch, put, delete's convenience methods though.
This is addition is currently being discussed here and here.
EDIT: I should state that this answered is based on a Laravel 5.1 install, so it might not be 100% applicable to you if you're on an older version.
You can use the json
method described here:
https://laravel.com/api/5.1/Illuminate/Foundation/Testing/TestCase.html#method_json
AS you can see the third parameter is an array of data witch in this case will be passed to the body of the request as json, and if you need to pass extra headers you can passed them as an array in the fourth parameter.
Example: (Inside your test class)
public function testRequestWithJSONBody()
{
$this->json(
'POST', //Method
'/', //Route
['key1' => 'value1', 'key2' => 'value2'], //JSON Body request
['headerKey1' => 'headerValue1','headerKey2' => 'headerValue2'] // Extra headers (optional)
)->seeStatusCode(200);
}
Hope this helps others.
I wanted to test JSON being posted from the browser to the back end. I wanted to put the raw json in phpunit so I did not have to recode the array, which introduced errors.
To do this I first converted the json object to a string in javascript (browser or client) and dumped it to the log:
console.log(JSON.stringify(post_data))
Next I copied and pasted that into phpunit test, then decoded it to an array. Then I simply sent that array to json:
$rawContent = '{"search_fields":{"vendor_table_id":"123","vendor_table_name":"","vendor_table_account_number":"","vendor_table_active":"null"},"table_name":"vendor_table"}';
$this->json('POST', '/vendors', json_decode($rawContent, true))
->seeJson([
'id' => 123,
]);
This was the only way it worked for me after implementing the other answers to this post, so I thought I would share. I am using laravel 5.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With