狠狠撸

狠狠撸Share a Scribd company logo
JSON Schema と 
API テスト 
2014/08/29 (Sat) 
YAPC::Asia Tokyo 2014 
清水 直樹
自己紹介 
? 清水 直樹 (@deme0607) 
? SWET @ DeNA 
? SWET: Software Engineer in Test 
? 2013年 4月 新卒入社
自己紹介② 
? テスト用のライブラリ 
? randexp-multibyte 
? https://rubygems.org/gems/randexp-multibyte 
? Rubyist Magazine 「Ruby 初心者の新卒エンジニアが 
gem パッケージ公開に至るまで」 
? http://magazine.rubyist.net/?0046- 
RandexMultibyteGem
今日の話 
? API 結合テストとは? 
? JSON Schema とは? 
? JSON Schema を API 結合テストに活用
础笔滨结合テスト
API (単体) テスト 
API 
Server 
Test 
1. テストデータをリクエスト 
として送信 
[request] 
GET api/user 
id: 1 
[response] 
id: 1 
name: deme0607 
email: deme0607@example.com 
2. レスポンスデータを期待結果 
と比較
それだけで十分? 
? API は様々なコンポーネントと結合して動作 
API 
Server 
User 
API 
Server 
DB 
Load Balancer / Reverse Proxy 
[request] [response]
础笔滨结合テスト 
? 実環境で動作しているAPIを実際のクライアン 
トと同じ経路からテスト 
API 
Server 
結合 
テスト 
API 
Server 
DB 
Load Balancer / Reverse Proxy 
[request] [response]
础笔滨结合テスト 実施フロー 
? 仕様ドキュメントから、正常系?異常系テストリク 
エストデータを作成 
? リクエストデータ ≒ テストケース 
? 仕様とリクエストデータから、期待するレスポンス 
を定義 
? テスト用クライアントからリクエストを送り、レス 
ポンスを検証
今日の話 
? API 結合テストとは? 
? JSON Schema とは? 
? JSON Schema を API 結合テストに活用
JSON Schema とは? 
? JSONで表現されるデータに対してデータ定義 
するSchemaを記述する枠組み 
? json-schema.org で公開されている 
? 現在、draft4
例 
? JSON データ 
! 
? 日本語の仕様 
! 
! 
? JSON Schema 
{ 
"id": 
12345678, 
"name": 
"Naoki 
Shimizu", 
"email": 
"deme0607@example.com" 
} 
! 
{ 
"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形式の文字列。
JSON Schema, 何が嬉しい? 
? データの検証にも使える 
? Machine Readable なデータの定義ができるので、Validatorの 
入力にできる 
? 仕様と実装の乖離が減る 
? 上記のようなValidatorを活用し、APIのリクエスト?レスポン 
スを検証 
? グローバル対応 
? JSONは機械にも人間にも読みやすい
? 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 
=> 
"deme0607@example.com", 
}; 
my 
$schema 
= 
decode_json($json_file); 
my 
$validator 
= 
JSV::Validator-?‐>new; 
my 
$result 
= 
$validator-?‐>validate($schema, 
$request); 
if 
($result) 
{ 
...
JSON Schema について詳しくは 
WEB+DB Press vol.82 特集1 「Web API デザインの鉄則」をご覧ください
今日の話 
? API 結合テストとは? 
? JSON Schema とは? 
? JSON Schema を API 結合テストに活用
(再) 础笔滨结合テスト 実施フロー 
? 仕様ドキュメントから、正常系?異常系テストリク 
エストデータを作成 
? リクエストデータ ≒ テストケース 
? 仕様とリクエストデータから、期待するレスポンス 
を定義 
? テスト用クライアントからリクエストを送り、レス 
ポンスを検証
API 仕様が JSON Schema 
で書かれていたら?
? 正常系?異常系のリクエストデータを自動生 
成できるかも? 
? 仕様とリクエストデータから、期待するレス 
ポンスも自動で定義できるかも? 
? 仕様から、クライアントも自動生成できるか 
も?
APIの結合テスト、 
全部自動でできちゃう?
そんなうまい话はありません
だが、今よりもっと楽する 
ことはできるはず
やりたいこと 
? JSON Schema で記述された API の仕様から 
? 正常系?異常系のリクエストデータ生成 
? 期待するレスポンスの定義 
? APIクライアントの生成
json-fuzz-generator 
? JSON Schema から、そのSchemaに対して正常 
系?異常系のデータを生成 
? Ruby のライブラリ 
? 異常系のデータはFuzzingに基いている 
? 誤りの含まれたデータを次々に入力するテスト 
手法
デモ
正常系データの生成 
# 
require 
"json-?‐fuzz-?‐generator" 
# 
JSON::Fuzz::Generator.default_param(schema_file) 
{ 
"id" 
=> 
0, 
"name" 
=> 
"hoge", 
"birthday” 
=> 
"1992-?‐06-?‐27" 
} 
JSON Schema の入力 
! 
{ 
"title": 
"Basic 
Schema", 
"type": 
"object", 
"properties": 
{ 
"id" 
: 
{ 
"type": 
"integer", 
"minimum": 
0 
}, 
"name": 
{ 
"type": 
"string" 
}, 
"birthday": 
{ 
"type": 
"string", 
"format": 
"date" 
} 
} 
} 
正常系データの出力
異常系データの生成 
[ 
["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"}, 
]
JSON Schema から自動生成 
したリクエストデータは、 
テストケースとして十分か?
残念ながら狈辞です
? ドメイン知識に基づくケースは生成できない 
? JSON Schemaではデータのフォーマット以上のこ 
とは定義できない 
? (例) 友達にメッセージを送るAPIで、「友達でない 
ユーザへのメッセージ送信」という異常系リクエス 
ト 
? ドメイン知識が必要なケースの設計に集中できる
础笔滨结合テスト 自動化への道 
? リクエストデータの生成 
? レスポンスの検証 
? APIクライアントの生成
リクエストデータの生成 
? フォーマットによるもの 
? json-fuzz-generator によって生成できる 
? ドメインの特性によるもの 
? JSON Schema からの生成は不可能
レスポンスの検証 
? フォーマット 
? JSON Schema による Validator で可能 
? perl-JSV (Perl), json-schema (Ruby) 
? APIのロジックに基づくもの 
? 例: リクエストで指定したユーザidのデータが返ってくる 
? JSON Schema からは不可能
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)
础笔滨结合テスト 自動化への道 
リクエスト生成レスポンス検証 
クライアント 
生成 
フォーマットドメイン特性フォーマットロジック 
fuzz-json-generator 
★ perl-JSV 
json-schema ★ jsonism 
ドメイン知識やロジック部分に集中したテスト設計が可能
まとめ 
? JSON Schema で仕様を記述すると開発でも 
テストでも利点がある 
? JSON Schema を使って、础笔滨结合テストの自 
動化に取り組んでいる 
? 自動化が進むと、より高品質な開発?テスト 
に集中できる
ありがとうございました 
? json-fuzz-generator 
? https://rubygems.org/gems/json-fuzz-generator 
? Twitter 
? https://twitter.com/deme0607
json-fuzz-generator要改善点 
? 正常系データの複数生成 
? 例: 各種境界値 
? 現状、最大値?最小値が定義されているような数値は範囲内のラン 
ダム値を返すような実装 
? 未対応のschema 
? pattern (正規表現) 
? patternにマッチする/しない文字列を自動生成 
? $ref (参照) 系
? 異常系パラメータの精度向上 
? Fuzzingでは桁あふれを起こしうる数値や文字化けを起こしやすい文 
字列を入力することが効果的 
? 現状は単純な異常値しか生成してない 
? stringを期待するデータにintegerを出力 
? 最大値?最小値の範囲から外れる値を出力 
? プロダクトに基づくパラメータの生成 
? 過去にバリデーション漏れ?問題を起こしたパラメータなど 
? ライブラリに同梱するのではなく、ユーザが動的に追加できる仕組 
み

More Related Content

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

  • 1. JSON Schema と API テスト 2014/08/29 (Sat) YAPC::Asia Tokyo 2014 清水 直樹
  • 2. 自己紹介 ? 清水 直樹 (@deme0607) ? SWET @ DeNA ? SWET: Software Engineer in Test ? 2013年 4月 新卒入社
  • 3. 自己紹介② ? テスト用のライブラリ ? randexp-multibyte ? https://rubygems.org/gems/randexp-multibyte ? Rubyist Magazine 「Ruby 初心者の新卒エンジニアが gem パッケージ公開に至るまで」 ? http://magazine.rubyist.net/?0046- RandexMultibyteGem
  • 4. 今日の話 ? API 結合テストとは? ? JSON Schema とは? ? JSON Schema を API 結合テストに活用
  • 6. API (単体) テスト API Server Test 1. テストデータをリクエスト として送信 [request] GET api/user id: 1 [response] id: 1 name: deme0607 email: deme0607@example.com 2. レスポンスデータを期待結果 と比較
  • 7. それだけで十分? ? API は様々なコンポーネントと結合して動作 API Server User API Server DB Load Balancer / Reverse Proxy [request] [response]
  • 8. 础笔滨结合テスト ? 実環境で動作しているAPIを実際のクライアン トと同じ経路からテスト API Server 結合 テスト API Server DB Load Balancer / Reverse Proxy [request] [response]
  • 9. 础笔滨结合テスト 実施フロー ? 仕様ドキュメントから、正常系?異常系テストリク エストデータを作成 ? リクエストデータ ≒ テストケース ? 仕様とリクエストデータから、期待するレスポンス を定義 ? テスト用クライアントからリクエストを送り、レス ポンスを検証
  • 10. 今日の話 ? API 結合テストとは? ? JSON Schema とは? ? JSON Schema を API 結合テストに活用
  • 11. JSON Schema とは? ? JSONで表現されるデータに対してデータ定義 するSchemaを記述する枠組み ? json-schema.org で公開されている ? 現在、draft4
  • 12. 例 ? JSON データ ! ? 日本語の仕様 ! ! ? JSON Schema { "id": 12345678, "name": "Naoki Shimizu", "email": "deme0607@example.com" } ! { "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形式の文字列。
  • 13. JSON Schema, 何が嬉しい? ? データの検証にも使える ? Machine Readable なデータの定義ができるので、Validatorの 入力にできる ? 仕様と実装の乖離が減る ? 上記のようなValidatorを活用し、APIのリクエスト?レスポン スを検証 ? グローバル対応 ? JSONは機械にも人間にも読みやすい
  • 14. ? 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 => "deme0607@example.com", }; my $schema = decode_json($json_file); my $validator = JSV::Validator-?‐>new; my $result = $validator-?‐>validate($schema, $request); if ($result) { ...
  • 15. JSON Schema について詳しくは WEB+DB Press vol.82 特集1 「Web API デザインの鉄則」をご覧ください
  • 16. 今日の話 ? API 結合テストとは? ? JSON Schema とは? ? JSON Schema を API 結合テストに活用
  • 17. (再) 础笔滨结合テスト 実施フロー ? 仕様ドキュメントから、正常系?異常系テストリク エストデータを作成 ? リクエストデータ ≒ テストケース ? 仕様とリクエストデータから、期待するレスポンス を定義 ? テスト用クライアントからリクエストを送り、レス ポンスを検証
  • 18. API 仕様が JSON Schema で書かれていたら?
  • 19. ? 正常系?異常系のリクエストデータを自動生 成できるかも? ? 仕様とリクエストデータから、期待するレス ポンスも自動で定義できるかも? ? 仕様から、クライアントも自動生成できるか も?
  • 23. やりたいこと ? JSON Schema で記述された API の仕様から ? 正常系?異常系のリクエストデータ生成 ? 期待するレスポンスの定義 ? APIクライアントの生成
  • 24. json-fuzz-generator ? JSON Schema から、そのSchemaに対して正常 系?異常系のデータを生成 ? Ruby のライブラリ ? 異常系のデータはFuzzingに基いている ? 誤りの含まれたデータを次々に入力するテスト 手法
  • 26. 正常系データの生成 # require "json-?‐fuzz-?‐generator" # JSON::Fuzz::Generator.default_param(schema_file) { "id" => 0, "name" => "hoge", "birthday” => "1992-?‐06-?‐27" } JSON Schema の入力 ! { "title": "Basic Schema", "type": "object", "properties": { "id" : { "type": "integer", "minimum": 0 }, "name": { "type": "string" }, "birthday": { "type": "string", "format": "date" } } } 正常系データの出力
  • 27. 異常系データの生成 [ ["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"}, ]
  • 28. JSON Schema から自動生成 したリクエストデータは、 テストケースとして十分か?
  • 30. ? ドメイン知識に基づくケースは生成できない ? JSON Schemaではデータのフォーマット以上のこ とは定義できない ? (例) 友達にメッセージを送るAPIで、「友達でない ユーザへのメッセージ送信」という異常系リクエス ト ? ドメイン知識が必要なケースの設計に集中できる
  • 31. 础笔滨结合テスト 自動化への道 ? リクエストデータの生成 ? レスポンスの検証 ? APIクライアントの生成
  • 32. リクエストデータの生成 ? フォーマットによるもの ? json-fuzz-generator によって生成できる ? ドメインの特性によるもの ? JSON Schema からの生成は不可能
  • 33. レスポンスの検証 ? フォーマット ? JSON Schema による Validator で可能 ? perl-JSV (Perl), json-schema (Ruby) ? APIのロジックに基づくもの ? 例: リクエストで指定したユーザidのデータが返ってくる ? JSON Schema からは不可能
  • 34. 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)
  • 35. 础笔滨结合テスト 自動化への道 リクエスト生成レスポンス検証 クライアント 生成 フォーマットドメイン特性フォーマットロジック fuzz-json-generator ★ perl-JSV json-schema ★ jsonism ドメイン知識やロジック部分に集中したテスト設計が可能
  • 36. まとめ ? JSON Schema で仕様を記述すると開発でも テストでも利点がある ? JSON Schema を使って、础笔滨结合テストの自 動化に取り組んでいる ? 自動化が進むと、より高品質な開発?テスト に集中できる
  • 37. ありがとうございました ? json-fuzz-generator ? https://rubygems.org/gems/json-fuzz-generator ? Twitter ? https://twitter.com/deme0607
  • 38. json-fuzz-generator要改善点 ? 正常系データの複数生成 ? 例: 各種境界値 ? 現状、最大値?最小値が定義されているような数値は範囲内のラン ダム値を返すような実装 ? 未対応のschema ? pattern (正規表現) ? patternにマッチする/しない文字列を自動生成 ? $ref (参照) 系
  • 39. ? 異常系パラメータの精度向上 ? Fuzzingでは桁あふれを起こしうる数値や文字化けを起こしやすい文 字列を入力することが効果的 ? 現状は単純な異常値しか生成してない ? stringを期待するデータにintegerを出力 ? 最大値?最小値の範囲から外れる値を出力 ? プロダクトに基づくパラメータの生成 ? 過去にバリデーション漏れ?問題を起こしたパラメータなど ? ライブラリに同梱するのではなく、ユーザが動的に追加できる仕組 み