JSON Schema と API テスト YAPC::Asia Tokyo 2014

39
JSON Schema API テスト 2014/08/29 (Sat) YAPC::Asia Tokyo 2014 清水 直樹

description

YAPC::Asia Tokyo 2014 Talk https://www.youtube.com/watch?v=bxNMk6XP2JA

Transcript of JSON Schema と API テスト YAPC::Asia Tokyo 2014

Page 1: JSON Schema と API テスト YAPC::Asia Tokyo 2014

JSON Schema と

API テスト

2014/08/29 (Sat) YAPC::Asia Tokyo 2014

清水 直樹

Page 2: JSON Schema と API テスト YAPC::Asia Tokyo 2014

自己紹介• 清水 直樹 (@deme0607)

• SWET @ DeNA

• SWET: Software Engineer in Test

• 2013年 4月 新卒入社

Page 3: JSON Schema と API テスト YAPC::Asia Tokyo 2014

自己紹介②• テスト用のライブラリ

• randexp-multibyte

• https://rubygems.org/gems/randexp-multibyte

• Rubyist Magazine 「Ruby 初心者の新卒エンジニアが

gem パッケージ公開に至るまで」

• http://magazine.rubyist.net/?0046-RandexMultibyteGem

Page 4: JSON Schema と API テスト YAPC::Asia Tokyo 2014

今日の話• API 結合テストとは?

• JSON Schema とは?

• JSON Schema を API 結合テストに活用

Page 5: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API結合テスト

Page 6: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API (単体) テスト

API Server

Test

[request] GET api/user id: 1

1. テストデータをリクエストとして送信

[response] id: 1 name: deme0607 email: [email protected]

2. レスポンスデータを期待結果と比較

Page 7: JSON Schema と API テスト YAPC::Asia Tokyo 2014

それだけで十分?• API は様々なコンポーネントと結合して動作

API Server

User

API Server

Load Balancer / Reverse Proxy

DB

[request] [response]

Page 8: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API結合テスト• 実環境で動作しているAPIを実際のクライアントと同じ経路からテスト

API Server

結合テスト

API Server

Load Balancer / Reverse Proxy

DB

[request] [response]

Page 9: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API結合テスト 実施フロー• 仕様ドキュメントから、正常系・異常系テストリクエストデータを作成

• リクエストデータ ≒ テストケース

• 仕様とリクエストデータから、期待するレスポンスを定義

• テスト用クライアントからリクエストを送り、レスポンスを検証

Page 10: JSON Schema と API テスト YAPC::Asia Tokyo 2014

今日の話• API 結合テストとは?

• JSON Schema とは?

• JSON Schema を API 結合テストに活用

Page 11: JSON Schema と API テスト YAPC::Asia Tokyo 2014

JSON Schema とは?• JSONで表現されるデータに対してデータ定義するSchemaを記述する枠組み

• json-schema.org で公開されている

• 現在、draft4

Page 12: JSON Schema と API テスト YAPC::Asia Tokyo 2014

例• JSON データ

!

• 日本語の仕様

!

!

• JSON Schema{      "id":  12345678,      "name":  "Naoki  Shimizu",      "email":  "[email protected]"  }

!{      "type":  "object",      "properties":  {          "id":  {              "type":  "integer",              "minimum":  10000000          },          "name":  {                "type":  "string"          },          "email":  {                "type":  "string",              "format":  "email"          }      }  }  

フィールド 型 詳細

id integer ユーザのidを表す。10000000以上の値。

name string ユーザの名前を表す文字列。

email stringユーザのメールアドレス。 RFC5322形式の文字列。

Page 13: JSON Schema と API テスト YAPC::Asia Tokyo 2014

JSON Schema, 何が嬉しい?• データの検証にも使える

• Machine Readable なデータの定義ができるので、Validatorの入力にできる

• 仕様と実装の乖離が減る

• 上記のようなValidatorを活用し、APIのリクエスト・レスポンスを検証

• グローバル対応

• JSONは機械にも人間にも読みやすい

Page 14: JSON Schema と API テスト YAPC::Asia Tokyo 2014

• Validator を使ってAPIサーバのリクエスト・レスポンスのSchemaとの整合性を検証

• perl-JSV: Perlのデータに対する JSON Schema Validator

• https://github.com/zigorou/perl-JSV

use  JSON;  use  JSV::Validator;      my  $request  =  {      id        =>  12345678,        name    =>  "Naoki  Shimizu",        email  =>  "[email protected]",  };      my  $schema  =  decode_json($json_file);      my  $validator  =  JSV::Validator-­‐>new;  my  $result  =  $validator-­‐>validate($schema,  $request);      if  ($result)  {          ...

Page 15: JSON Schema と API テスト YAPC::Asia Tokyo 2014

JSON Schema について詳しくは

WEB+DB Press vol.82 特集1 「Web API デザインの鉄則」をご覧ください

Page 16: JSON Schema と API テスト YAPC::Asia Tokyo 2014

今日の話• API 結合テストとは?

• JSON Schema とは?

• JSON Schema を API 結合テストに活用

Page 17: JSON Schema と API テスト YAPC::Asia Tokyo 2014

(再) API結合テスト 実施フロー• 仕様ドキュメントから、正常系・異常系テストリクエストデータを作成

• リクエストデータ ≒ テストケース

• 仕様とリクエストデータから、期待するレスポンスを定義

• テスト用クライアントからリクエストを送り、レスポンスを検証

Page 18: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API 仕様が JSON Schema で書かれていたら?

Page 19: JSON Schema と API テスト YAPC::Asia Tokyo 2014

• 正常系・異常系のリクエストデータを自動生成できるかも?

• 仕様とリクエストデータから、期待するレスポンスも自動で定義できるかも?

• 仕様から、クライアントも自動生成できるかも?

Page 20: JSON Schema と API テスト YAPC::Asia Tokyo 2014

APIの結合テスト、 全部自動でできちゃう?

Page 21: JSON Schema と API テスト YAPC::Asia Tokyo 2014

そんなうまい話はありません

Page 22: JSON Schema と API テスト YAPC::Asia Tokyo 2014

だが、今よりもっと楽する ことはできるはず

Page 23: JSON Schema と API テスト YAPC::Asia Tokyo 2014

やりたいこと• JSON Schema で記述された API の仕様から

• 正常系・異常系のリクエストデータ生成

• 期待するレスポンスの定義

• APIクライアントの生成

Page 24: JSON Schema と API テスト YAPC::Asia Tokyo 2014

json-fuzz-generator• JSON Schema から、そのSchemaに対して正常系・異常系のデータを生成

• Ruby のライブラリ

• 異常系のデータはFuzzingに基いている

• 誤りの含まれたデータを次々に入力するテスト手法

Page 25: JSON Schema と API テスト YAPC::Asia Tokyo 2014

デモ

Page 26: JSON Schema と API テスト YAPC::Asia Tokyo 2014

正常系データの生成

#  require  "json-­‐fuzz-­‐generator"      #  JSON::Fuzz::Generator.default_param(schema_file)      {      "id"              =>  0,      "name"          =>  "hoge",      "birthday”  =>  "1992-­‐06-­‐27"  }

!{      "title":  "Basic  Schema",      "type":  "object",      "properties":  {          "id"  :  {              "type":  "integer",              "minimum":  0          },          "name":  {              "type":  "string"          },          "birthday":  {              "type":  "string",              "format":  "date"          }      }  }

JSON Schema の入力

正常系データの出力

Page 27: JSON Schema と API テスト YAPC::Asia Tokyo 2014

異常系データの生成[      ["sample",  "array"],      true,      73,      nil,      0.34259093948835795,      "hoge",      {"id"=>"a",  "name"=>"hoge",                                                                                                "birthday"=>"1992-­‐06-­‐27"},      {"id"=>"1",  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0.1,  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>["sample",  "array"],  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>false,  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>nil,  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0.0,  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>{},  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>"hoge",  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>-­‐1,  "name"=>"hoge",          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>["sample",  "array"],          “birthday"=>"1992-­‐06-­‐27"},  !

   {"id"=>0,  "name"=>true,          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>97,  "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>nil,          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>0.7547537108664406,          "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>{},  "birthday"=>"1992-­‐06-­‐27"},      {"id"=>0,  "name"=>"hoge",          "birthday"=>["sample",  "array"]},      {"id"=>0,  "name"=>"hoge",  "birthday"=>false},      {"id"=>0,  "name"=>"hoge",  "birthday"=>11},      {"id"=>0,  "name"=>"hoge",  "birthday"=>nil},      {"id"=>0,  "name"=>"hoge",          "birthday"=>0.5380909041403419},      {"id"=>0,  "name"=>"hoge",  "birthday"=>{}},      {"id"=>0,  "name"=>"hoge",          "birthday"=>"2010-­‐01-­‐32"},      {"id"=>0,  "name"=>"hoge",          "birthday"=>"n2010-­‐01-­‐01"},      {"id"=>0,  "name"=>"hoge",          "birthday"=>"2010-­‐1-­‐01"},      {"id"=>0,  "name"=>"hoge",          "birthday"=>"2010-­‐01-­‐1"},      {"id"=>0,  "name"=>"hoge",          "birthday"=>"2010-­‐01-­‐01n"},  ]  

Page 28: JSON Schema と API テスト YAPC::Asia Tokyo 2014

JSON Schema から自動生成 したリクエストデータは、 テストケースとして十分か?

Page 29: JSON Schema と API テスト YAPC::Asia Tokyo 2014

残念ながらNoです

Page 30: JSON Schema と API テスト YAPC::Asia Tokyo 2014

• ドメイン知識に基づくケースは生成できない

• JSON Schemaではデータのフォーマット以上のことは定義できない

• (例) 友達にメッセージを送るAPIで、「友達でないユーザへのメッセージ送信」という異常系リクエスト

• ドメイン知識が必要なケースの設計に集中できる

Page 31: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API結合テスト 自動化への道• リクエストデータの生成

• レスポンスの検証

• APIクライアントの生成

Page 32: JSON Schema と API テスト YAPC::Asia Tokyo 2014

リクエストデータの生成• フォーマットによるもの

• json-fuzz-generator によって生成できる

• ドメインの特性によるもの

• JSON Schema からの生成は不可能

Page 33: JSON Schema と API テスト YAPC::Asia Tokyo 2014

レスポンスの検証• フォーマット

• JSON Schema による Validator で可能

• perl-JSV (Perl), json-schema (Ruby)

• APIのロジックに基づくもの

• 例: リクエストで指定したユーザidのデータが返ってくる

• JSON Schema からは不可能

Page 34: JSON Schema と API テスト YAPC::Asia Tokyo 2014

APIクライアントの自動生成• jsonism で生成可能 (Ruby ライブラリ)

client  =  Jsonism::Client.new(schema:  schema)  client.methods(false)    #=>  [:create_app,  :delete_app,  :info_app,  :list_app,  :update_app]      #  GET  /apps  client.list_app      #  GET  /apps/1  client.info_app(id:  1)      #  POST  /apps  client.create_app(name:  "alpha")      #  PATCH  /apps/1  client.update_app(id:  1,  name:  "bravo")      #  DELETE  /apps/1  client.delete_app(id:  1)

Page 35: JSON Schema と API テスト YAPC::Asia Tokyo 2014

API結合テスト 自動化への道

リクエスト生成 レスポンス検証クライアント 生成

フォーマット ドメイン特性 フォーマット ロジック

fuzz-json-generator ★ perl-JSV

json-schema ★ jsonism

ドメイン知識やロジック部分に集中したテスト設計が可能

Page 36: JSON Schema と API テスト YAPC::Asia Tokyo 2014

まとめ• JSON Schema で仕様を記述すると開発でもテストでも利点がある

• JSON Schema を使って、API結合テストの自動化に取り組んでいる

• 自動化が進むと、より高品質な開発・テストに集中できる

Page 37: JSON Schema と API テスト YAPC::Asia Tokyo 2014

ありがとうございました• json-fuzz-generator

• https://rubygems.org/gems/json-fuzz-generator

• Twitter

• https://twitter.com/deme0607

Page 38: JSON Schema と API テスト YAPC::Asia Tokyo 2014

json-fuzz-generator要改善点• 正常系データの複数生成

• 例: 各種境界値

• 現状、最大値・最小値が定義されているような数値は範囲内のランダム値を返すような実装

• 未対応のschema

• pattern (正規表現)

• patternにマッチする/しない文字列を自動生成

• $ref (参照) 系

Page 39: JSON Schema と API テスト YAPC::Asia Tokyo 2014

• 異常系パラメータの精度向上

• Fuzzingでは桁あふれを起こしうる数値や文字化けを起こしやすい文字列を入力することが効果的

• 現状は単純な異常値しか生成してない

• stringを期待するデータにintegerを出力

• 最大値・最小値の範囲から外れる値を出力

• プロダクトに基づくパラメータの生成

• 過去にバリデーション漏れ・問題を起こしたパラメータなど

• ライブラリに同梱するのではなく、ユーザが動的に追加できる仕組み