PHPUnit + openapi-validator で「スキーマが正、実装が追従」にする

### 今回話したいこと

（API開発チームのひとコマ）

:woman: 「『実装が正、スキーマが追従』って無理だよね」

:man_tone2: 「『スキーマが正、実装が追従』するようにしたいよね」

※本記事はLaravel/Vue.js勉強会#10の発表資料です

---
### しましょう

PHPUnitで、APIがスキーマと異なるレスポンスを返したら、落ちるテストを書きましょう :red_circle:

---
### そのために必要なもの

- OpenApi
- redoc-cli
- PHPUnit
- openapi-validator

---
### OpenApi

スキーマを書くための すごい yaml (JSON)。

まぁまぁ覚えることが多い。

https://swagger.io/docs/specification/about/

---
こんな感じ

```yaml
openapi: 3.0.2
info:
  title: 蔵書管理システムAPIドキュメント
  description: 蔵書管理システムのAPIドキュメントです
  version: 1.0.0
servers:
  - url: https://example.com/api
    description: 本番環境
paths:
  /books:
    get:
      summary: 本の一覧
      description: |
        登録してある本の一覧を取得できます
      operationId: getBooks
      parameters:
        - name: borrow
          in: query
          description: |
            貸出中を含める
          required: false
          schema:
            type: boolean
            example: true
      responses:
        200:
          description: 成功
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/book'
components:
  schemas:
    book:
      title: Book
      description: Bookのスキーマです。
      required:
        - id
        - title
        - created_at
        - updated_at
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        created_at:
          type: string
        updated_at:
          type: string
      example:
        id: "1"
        title: "Love beautiful code"
        created_at: "2019-07-08 15:22:15"
        updated_at: "2019-07-08 15:22:15"
```

Q. なんかよくわかんないんだけど…
A. 慣れれば読めるようになります！頑張ってください！！

---
### redoc-cli

さっきのよくわかんない yaml をhtmlにしてくれるやつ

コマンド一発でドキュメントを作ってくれるから便利

`redoc-cli bundle ./resources/docs/apiV1.yml -o ~/book_sample.html`

https://github.com/Redocly/redoc/tree/master/cli

---
こんな感じ
![Screenshot from 2019-07-16 23-16-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158622/e0df7073-c27b-7309-65c2-394f0354c418.png)

---
### openapi-validator 
OpenApi の yaml をPHPUnitに取りこむやつ 

[イメージ図]

```
response => Validator = (OK) => Green
                      = (NG) => Red
```

https://gitlab.com/mmalawski/openapi-validator

---
### 実装してみる

(Laravelを使う前提)

1. OpenApi でスキーマを書く
2. PHPUnit (openapi-validator) でそのスキーマを呼び出す
3. レスポンスとスキーマが違ってたらRED

---
### 1. OpenApi でスキーマを書く

頑張りましょう！

(ドキュメント見つつ悪戦苦闘するしか無いような気が… :innocent: )

https://swagger.io/docs/specification/about

今回は出来上がったスキーマは `resources/docs/apiV1.yml` に置きます

---
### 2. PHPUnit (openapi-validator) でそのスキーマを呼び出す

こんな感じです

```php
<?php

namespace Tests\Feature;

use Tests\TestCase;
use Mmal\OpenapiValidator\Validator;
use Symfony\Component\Yaml\Yaml;


class ApiV1Test extends TestCase
{
    /** @var Validator */
    static $openApiValidator;

    public static function setUpBeforeClass()
    {
        parent::setUpBeforeClass();
        self::$openApiValidator = new Validator(Yaml::parse(file_get_contents(resource_path('docs/apiV1.yml'))));
    }

    /**
     * @test
     */
    public function getBooks()
    {
        $response = $this->get('api/book');
        
        $result = self::$openApiValidator->validate('getBooks', 200, json_decode($response->getContent(), true));
        $this->assertFalse($result->hasErrors(), $result);
    }
}
```

1. テスト前に `$openApiValidator` としてOpenApiを読み込んで
2. テストで「OpenApi上のレスポンスの定義 / 期待するステータスコード /レスポンス」を `$openApiValidator` を渡す
3. Green or Red

---
### 実際にやってみる（Red）

レスポンスから `updated_at` を消してみると…

![Screenshot from 2019-07-16 23-01-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158622/5ab9cbad-2d91-4e4d-04b1-e0c5475788d4.png)

あるはずのプロパティが無いと怒られる！Red！！

---
### 実際にやってみる（Green）

レスポンスに `updated_at` を追加してみると…

![Screenshot from 2019-07-16 22-59-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158622/3a200fd1-2c00-e114-3c92-586b25800588.png)

レスポンスとスキーマの定義が同じ！Green！！

---
### テストが全部通った後に

redoc-cli でスキーマを html にして公開！ 

---
### 最後に

何が辛いって、OpenApi(yaml)書くのがとにかく辛いです (^o^)

既存でAPIがあるとなおさらしんどいです (^o^)(^o^)