Weitere ähnliche Inhalte Ähnlich wie JSON Schema と API テスト YAPC::Asia Tokyo 2014 (20) Kürzlich hochgeladen (10) JSON Schema と API テスト YAPC::Asia Tokyo 20141. 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]
9. API結合テスト 実施フロー
• 仕様ドキュメントから、正常系・異常系テストリク
エストデータを作成
• リクエストデータ ≒ テストケース
• 仕様とリクエストデータから、期待するレスポンス
を定義
• テスト用クライアントからリクエストを送り、レス
ポンスを検証
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)
{
...
16. 今日の話
• API 結合テストとは?
• JSON Schema とは?
• JSON Schema を API 結合テストに活用
17. (再) API結合テスト 実施フロー
• 仕様ドキュメントから、正常系・異常系テストリク
エストデータを作成
• リクエストデータ ≒ テストケース
• 仕様とリクエストデータから、期待するレスポンス
を定義
• テスト用クライアントからリクエストを送り、レス
ポンスを検証
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"},
]
30. • ドメイン知識に基づくケースは生成できない
• JSON Schemaではデータのフォーマット以上のこ
とは定義できない
• (例) 友達にメッセージを送るAPIで、「友達でない
ユーザへのメッセージ送信」という異常系リクエス
ト
• ドメイン知識が必要なケースの設計に集中できる
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)
36. まとめ
• JSON Schema で仕様を記述すると開発でも
テストでも利点がある
• JSON Schema を使って、API結合テストの自
動化に取り組んでいる
• 自動化が進むと、より高品質な開発・テスト
に集中できる
39. • 異常系パラメータの精度向上
• Fuzzingでは桁あふれを起こしうる数値や文字化けを起こしやすい文
字列を入力することが効果的
• 現状は単純な異常値しか生成してない
• stringを期待するデータにintegerを出力
• 最大値・最小値の範囲から外れる値を出力
• プロダクトに基づくパラメータの生成
• 過去にバリデーション漏れ・問題を起こしたパラメータなど
• ライブラリに同梱するのではなく、ユーザが動的に追加できる仕組
み