如何断言由两个 JSON 字符串表示的数据相等?

Jea*_*uwy 2 phpunit json unit-testing laravel

我第一次尝试对我的项目使用单元测试,但是我被一个断言模型正确存储的测试阻止了。

这是我要测试的 API 控制器:

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);
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试:

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());
} 
Run Code Online (Sandbox Code Playgroud)

这是结果:

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}]}'
Run Code Online (Sandbox Code Playgroud)

事实上,结果是正确的。但顺序不一样。我在测试中做错了什么?

谢谢。

loc*_*inz 6

对于您的示例,您可以使用随附的断言phpunit/phpunit

或者

assertJsonFileEqualsJsonFile()

但是,当我 - 举个例子 - 将两个 JSON 字符串提取到单独的文件中时

  • expected.json
  • actual.json

然后格式化的PhpStorm JSON与option+ command+L和运行以下测试

use PHPUnit\Framework;

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

        $this->assertJsonFileEqualsJsonFile($expected, $actual);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试失败

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,
Run Code Online (Sandbox Code Playgroud)

你可以看到实际上两个JSON字符串并不相等,至少字段的顺序是不一样的。断言输出的问题在于它并没有真正的帮助,仅仅是因为断言只是比较了两个字符串。

assertEquals()

尽管如此,当我们json_decode()使用文件的内容然后assertEquals()像这样断言时:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后测试再次失败,但它实际上是输出更有帮助:

Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
-    'deleted_at' => null
Run Code Online (Sandbox Code Playgroud)

如您所见,数据之间存在实际差异 - 根对象节点缺少deleted_at值为 的字段null

注意所以,问题归结为

  • 你想断言两个 JSON 字符串是相等的还是
  • 是否要断言两个 JSON 字符串表示的数据相等

根据哪个对您重要,选择对您更有意义的断言。