📝
Laravelのjsonに対するテストのetcメソッド
// api/sampleで下記を返すとする
return response()->json([
'sample' => true,
'sampleLevel1' => [
'name' => 'aaa',
'number' => 1,
],
]);
このようなルートがあるとしてこのルートへのtestを書く時のetcメソッドについて比較してみた。
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
);
}
etc無し。
FAIL Tests\Feature\Api\SampleTest
⨯ json etc
---
• Tests\Feature\Api\SampleTest > json etc
Unexpected properties were found on the root level.
Failed asserting that two arrays are identical.
at tests/Feature/Api/SampleTest.php:20
16▕ $response->assertJson(
17▕ fn (AssertableJson $json) =>
18▕ $json
19▕ ->where('sample', true)
➜ 20▕ );
21▕ }
22▕ }
23▕
--- Expected
+++ Actual
@@ @@
-Array &0 ()
+Array &0 (
+ 1 => 'sampleLevel1'
+)
Tests: 1 failed
Time: 0.65s
当然NGとなる
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
+ ->etc()
);
}
etcをトップに追加
PASS Tests\Feature\Api\SampleTest
✓ json etc
Tests: 1 passed
Time: 0.65s
通る
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
+ ->has('sampleLevel1')
+ ->where('sampleLevel1.name', 'aaa')
->etc()
);
}
Level1のname属性のアサート追加
PASS Tests\Feature\Api\SampleTest
✓ json etc
Tests: 1 passed
Time: 0.59s
一応通る
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
->has('sampleLevel1')
->where('sampleLevel1.name', 'aaa')
- ->etc()
);
}
etcメソッドを消す。
期待しているのはテストが失敗すること
- sampleLevel1.numberを無視しているから
PASS Tests\Feature\Api\SampleTest
✓ json etc
Tests: 1 passed
Time: 0.66s
もちろん通る。
etcメソッドを理解する
上記の例では、アサートのチェーンの最後でetcメソッドを呼び出したことに気づかれた方もいらっしゃるでしょう。このメソッドはLaravelへJSONオブジェクト中に他の属性が存在する可能性があることを伝えます。etcメソッドが使用されていない場合は、JSONオブジェクトに他の属性が存在していることをアサートしていないため、テストは失敗します。
この動作の意図は、属性に対して明示的にアサートを行うか、etc メソッドで追加の属性を明示的に許可することで、JSONレスポンスで意図せず機密情報を公開してしまうことを防ぐことにあります。
しかし、etcメソッドをアサートのチェーンに含めないからといっても、JSONオブジェクトのネストに含まれる配列へ、追加の属性が追加されないわけではないことを認識しておく必要はあります。etcメソッドは、etcメソッドを呼び出したネストレベルで、追加の属性が存在しないことを保証するだけです。
etcメソッドを呼び出したネストレベルで実行されるからsampleLevel1と同階層で呼び出したらsampleLevel1の中身は考慮しない。
期待しているテストが失敗するコードは
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
->has(
'sampleLevel1',
fn (AssertableJson $json) =>
$json
->where('name', 'aaa')
)
);
• Tests\Feature\Api\SampleTest > json etc
Unexpected properties were found in scope [sampleLevel1].
Failed asserting that two arrays are identical.
at tests/Feature/Api/SampleTest.php:22
18▕ 'sampleLevel1',
19▕ fn (AssertableJson $json) =>
20▕ $json
21▕ ->where('name', 'aaa')
➜ 22▕ )
23▕ );
24▕ }
25▕ }
26▕
1 tests/Feature/Api/SampleTest.php:23
Illuminate\Testing\TestResponse::assertJson()
--- Expected
+++ Actual
@@ @@
-Array &0 ()
+Array &0 (
+ 1 => 'number'
+)
Tests: 1 failed
Time: 0.65s
number属性を何もしてないから失敗するが、これが期待している
class SampleTest extends TestCase
{
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
->has(
'sampleLevel1',
fn (AssertableJson $json) =>
$json
->where('name', 'aaa')
+ ->where('number', 1)
)
);
}
}
class SampleTest extends TestCase
{
public function testJsonEtc(): void
{
$response = $this->get('api/sample');
$response->assertJson(
fn (AssertableJson $json) =>
$json
->where('sample', true)
->has(
'sampleLevel1',
fn (AssertableJson $json) =>
$json
->where('name', 'aaa')
+ ->etc()
)
);
}
}
etcのネストレベルに注意しないと無いことを許容したい属性やテストし忘れてる属性が漏れてもったいない
Discussion