Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use Laravel's `call` method to send raw JSON data in a unit test?

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?

like image 876
Ben Harold Avatar asked Oct 24 '14 20:10

Ben Harold


3 Answers

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.

like image 133
kidshenlong Avatar answered Sep 21 '22 15:09

kidshenlong


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.

like image 26
David Cadavid Avatar answered Sep 18 '22 15:09

David Cadavid


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.

like image 44
Iannazzi Avatar answered Sep 20 '22 15:09

Iannazzi