Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I assert that data represented by two JSON strings is equal?

For the first time I'm trying to use unit tests for my projects, but I'm blocked with a test asserting that a model is properly stored.

This is the API controller I want to test:

public function store(QuestionFormRequest $request)
{
    $questionRequest = $request->question();

    $question = new Question($questionRequest);
    $question->save();

    $question->answers()->createMany($questionRequest['answers']);

    return response()->json($question->load('answers'), 201);
}

This is my test:

public function it_can_store_a_question()
{
    $surveyFactory = factory(Survey::class)->create();
    $themeFactory = factory(Theme::class)->create();
    $pillarFactory = factory(Pillar::class)->create();

    $questionToStore = [
        'survey_id' => $surveyFactory->id,
        'theme_id' => $themeFactory->id,
        'pillar_id' => $pillarFactory->id,
        'name' => 'question',
        'type' => 'simple',
        'answers' => [
            [
                'label' => 'reponse1',
                'points' => '3',
            ],
            [
                'label' => 'reponse2',
                'points' => '5',
            ]
        ]
    ];

    $response = $this->post('/api/1.0/question', $questionToStore);
    $response->assertStatus(201);

    $expectedQuestion = Question::with('answers')->get()->first();
    $this->assertEquals(json_encode($expectedQuestion), $response->getContent());
} 

This is the result:

Failed asserting that two strings are equal.
Expected :'{"id":1,"survey_id":1,"theme_id":1,"pillar_id":1,"name":"question","type":"simple","created_at":"2017-08-29 08:54:45","updated_at":"2017-08-29 08:54:45","deleted_at":null,"answers":[{"id":1,"question_id":1,"label":"reponse1","points":3,"description":"","created_at":"2017-08-29 08:54:45","updated_at":"2017-08-29 08:54:45","deleted_at":null},{"id":2,"question_id":1,"label":"reponse2","points":5,"description":"","created_at":"2017-08-29 08:54:45","updated_at":"2017-08-29 08:54:45","deleted_at":null}]}'
Actual   :'{"survey_id":1,"theme_id":1,"pillar_id":1,"name":"question","type":"simple","updated_at":"2017-08-29 08:54:45","created_at":"2017-08-29 08:54:45","id":1,"answers":[{"id":1,"question_id":1,"label":"reponse1","points":3,"description":"","created_at":"2017-08-29 08:54:45","updated_at":"2017-08-29 08:54:45","deleted_at":null},{"id":2,"question_id":1,"label":"reponse2","points":5,"description":"","created_at":"2017-08-29 08:54:45","updated_at":"2017-08-29 08:54:45","deleted_at":null}]}'

In fact, the result is right. But not in the same order. What am I doing wrong in my test?

Thanks.

like image 416
Jean-louis Gouwy Avatar asked Feb 01 '26 12:02

Jean-louis Gouwy


1 Answers

For your example you could use assertions shipped with phpunit/phpunit:

  • assertJsonFileEqualsJsonFile()
  • assertJsonStringEqualsJsonString()
  • assertJsonStringEqualsJsonFile()

or

  • assertEquals()

assertJsonFileEqualsJsonFile()

However, when I - for the sake of an example - extract the two JSON strings into separate files

  • expected.json
  • actual.json

and then format the JSON in PhpStorm with option + command + L and run the following test

use PHPUnit\Framework;

final class JsonTest extends Framework\TestCase
{
    public function testJsonEquals()
    {
        $expected = __DIR__ . '/expected.json';
        $actual = __DIR__ . '/actual.json';

        $this->assertJsonFileEqualsJsonFile($expected, $actual);
    }
}

the test fails with

Failed asserting that '{\n
  "id": 1,\n
  "survey_id": 1,\n
  "theme_id": 1,\n
  "pillar_id": 1,\n
  "name": "question",\n
  "type": "simple",\n
  "created_at": "2017-08-29 08:54:45",\n
  "updated_at": "2017-08-29 08:54:45",\n
  "deleted_at": null,\n
  "answers": [\n
    {\n
      "id": 1,\n
      "question_id": 1,\n
      "label": "reponse1",\n
      "points": 3,\n
      "description": "",\n
      "created_at": "2017-08-29 08:54:45",\n
      "updated_at": "2017-08-29 08:54:45",\n
      "deleted_at": null\n
    },\n
    {\n
      "id": 2,\n
      "question_id": 1,\n
      "label": "reponse2",\n
      "points": 5,\n
      "description": "",\n
      "created_at": "2017-08-29 08:54:45",\n
      "updated_at": "2017-08-29 08:54:45",\n
      "deleted_at": null\n
    }\n
  ]\n
}\n
' matches JSON string "{
  "survey_id": 1,
  "theme_id": 1,
  "pillar_id": 1,
  "name": "question",
  "type": "simple",
  "updated_at": "2017-08-29 08:54:45",
  "created_at": "2017-08-29 08:54:45",
  "id": 1,
  "answers": [
    {
      "id": 1,
      "question_id": 1,
      "label": "reponse1",
      "points": 3,
      "description": "",
      "created_at": "2017-08-29 08:54:45",
      "updated_at": "2017-08-29 08:54:45",
      "deleted_at": null
    },
    {
      "id": 2,
      "question_id": 1,
      "label": "reponse2",
      "points": 5,
      "description": "",
      "created_at": "2017-08-29 08:54:45",
      "updated_at": "2017-08-29 08:54:45",
      "deleted_at": null
    }
  ]
}
".
--- Expected
+++ Actual
@@ @@
 {
-  "id": 1,
   "survey_id": 1,
   "theme_id": 1,
   "pillar_id": 1,
   "name": "question",
   "type": "simple",
+  "updated_at": "2017-08-29 08:54:45",
   "created_at": "2017-08-29 08:54:45",
-  "updated_at": "2017-08-29 08:54:45",
-  "deleted_at": null,
+  "id": 1,

What you can see is that in fact the two JSON strings aren't equal, at least the order of the fields isn't the same. The problem with the output of the assertion is that it's not really helpful, simply because the assertion just compares the two strings.

assertEquals()

Nonetheless, when we json_decode() the content of the files and then assert using assertEquals() like this:

use PHPUnit\Framework;

final class JsonTest extends Framework\TestCase
{
    public function testJsonEquals()
    {
        $expected = json_decode(file_get_contents(__DIR__ . '/expected.json'), true);
        $actual = json_decode(file_get_contents(__DIR__ . '/actual.json'), true);

        $this->assertEquals($expected, $actual);
    }
}

then the test fails again, but with output that is actually much more helpful:

Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
-    'deleted_at' => null

As you can see, there's an actual difference between the data - the root object node is missing a deleted_at field with a value of null.

Note So, the question boils down to

  • do you want to assert that two JSON strings are equal or
  • do you want to assert that the data represented by two JSON strings are equal

Depending on which is important to you, pick whichever assertion makes more sense to you.

like image 89
localheinz Avatar answered Feb 04 '26 05:02

localheinz