[Laravel]assertableJsonでJson形式のままテストする

# はじめに

Laravelによる初めてのAPIのテストコードを書いていた

あらかた完成したタイミングで、ふと同僚にコードを見てもらった時に

「Jsonのままテストできるよ」

と指摘され、調べたときのメモ

## 環境

- PHP 8.0.5
- Laravel 8.5.0

## テスト前の準備とテスト内容

### Model

まずはApp/Models/User.phpにてユーザーに紐づく注文を取得できるようにリレーションを張る

```php
/**
 * ユーザーに紐づく注文の取得.
 *
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function orders()
{
    return $this->hasMany(Order::class);
}
```

### Controller

Controllerのindexメソッドで返ってくるAPIのJSONデータをテスト

```php
/**
 * ユーザーに紐づく注文の取得.
 *
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function index()
{
		// ログインユーザーの注文の取得
    $orders = User::findOrFail(Auth::id())
        ->orders()
        ->get();

    return response()->json([
        'success' => true,
        'orders' => $orders->map(function ($orders) {
            return [
                'uuid' => $orders->uuid,
                'name' => $orders->name,
                'phone_number' => $orders->phone_number,
                'updated_at' => $orders->updated_at->format('Y-m-d H:i:s.u'),
            ];
        }),
    ]);
}
```

以下のJsonデータが返ってくるAPIのテストをする

ordersは多重配列構造。

### 返ってくるJsooデータ

```php
{
    success : true,
    orders : 
    [
        uuid: ...
		name: ...
		phone_number: ...
		updated_at: ...
    ],
    [
        uuid: ...
		name: ...
		phone_number: ...
		updated_at: ...
    ],
	...
}
```

# 当初のテストコード

同僚に初めて見せたテストコードがこちら。

```php
/**
 * ユーザーに紐づく注文の取得のテスト.
 *
 * @void
 */
protected function testIndex()
{
	// ユーザー、注文情報の取得
	$user = User::findOrFail(1); // ここは仮でIDおいてます。
    $orders = $user->orders;

	// API呼び出し(routeの名前は仮)
    $response = $this->actingAs($user)->getJson(route('api.orders.index'));

	// jsonデータを配列に変換
    $contentArray = json_decode((string) $response->getContent(), true);
    $contentArrayCount = count($contentArray['orders']);

	// 以下ひたすらテスト
	$this->assertCount(2, $contentArrayCount);
	$this->assertIsBool($contentArray['success']);
	$this->assertIsArray($contentArray['orders'])
	$this->assertSame('true', $contentArray['success']);
	...
	...
}
```

API呼び出し後に取得値をJsonから配列に置き換えて、PHPUnitにあるメソッドを使ってテストを実行している

# どうやってやるの？

では、Jsonのままテストをするにはどうするのさ、と調べるとLaravelドキュメントの[**HTTPテスト**]([https://readouble.com/laravel/8.x/ja/http-tests.html#response-assertions](https://readouble.com/laravel/8.x/ja/http-tests.html#response-assertions))を見て即解決した。

https://readouble.com/laravel/8.x/ja/http-tests.html#response-assertions

**assertJsonCount**だとか**assertJson**といういかにも、なメソッドを発見。

さらにassertJsonと[assertableJson]([https://readouble.com/laravel/8.x/ja/http-tests.html#fluent-json-testing](https://readouble.com/laravel/8.x/ja/http-tests.html#fluent-json-testing))を使うと、今回のような多重配列のJsonでも柔軟なテストが可能であった。

https://readouble.com/laravel/8.x/ja/http-tests.html#fluent-json-testing

assertJsonについて実用的な書き方は以下記事で書いていますので、初めてAPIテストコードを書く方のお役に立てると嬉しいです。

https://qiita.com/TkTkTkTkTako/items/1d2f6071ec5722211074

# 修正後のテストコード

```php
/**
 * ユーザーに紐づく注文の取得のテスト.
 *
 * @void
 */
protected function testIndex()
{
	// ユーザー、注文情報の取得
	$user = User::findOrFail(1); // ここは仮でIDおいてます。
    $orders = $user->orders;

	// API呼び出し(routeの名前は仮)
    $response = $this->actingAs($user)->getJson(route('api.orders.index'));

	// 以下ひたすらテスト
	$this->assertJsonCount(2);
	$response->assertJson(function (AssertableJson $json) use ($contentArrayCount) {
	    $json->whereAllType([
	          'success' => 'boolean',
	          'orders' => 'array',
	    ]);
		$json->where('success', true);
	}, true);
	...
	...
}
```

Jsonのままテストができるので、配列に置き換える必要がなくなった。

# おわりに

初めての作業であれば、ちゃんと公式ドキュメントは読もう。

得るものは大きかった。