FrisbyでREST APIのテスト自動化を試みる
REST APIのテストがめんどくさい、自動化したい、と調べていたら、FrisbyというREST APIのテストフレームワークのことを知ったので試してみた。
シナリオが書けるREST APIのテストツールを探した
意外になくて驚いた。
Frisbyを選んだ理由は、使っている人が割といて情報も多そうだということと、簡単にテストコードが記述できそうという理由から。
ちなみに、Frisbyの読み方は「フリスビー」です。
Get Started
1. インストールする
公式ドキュメントに沿って、FrisbyとJasmineをインストールします。
% npm install --save-dev frisby % npm install -g jasmine-node
2. テストを書く
テストコードを置くディレクトリを作成します。
% mkdir api_test % cd api_test
ディレクトリを作ったら、テストコードを置きます。テストコードは***_spec.js
というファイル名にします。
今回は郵便番号-住所検索APIというAPIを使わせていただいて、ステータスコードを確認するテストコードを書いてみました。
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .toss();
3. テストを実行する
jasmine-node ディレクトリパス
で実行します。
成功したらこうなる。
% jasmine-node . Finished in 0.112 seconds 1 test, 14 assertions, 0 failures, 0 skipped
失敗するとこうなる。
% jasmine-node . F Failures: 1) Frisby Test: 住所検索 [ GET http://api.zipaddress.net/?zipcode=1000000 ] Message: Expected 200 to equal 500. Stacktrace: Error: Expected 200 to equal 500. at .<anonymous> (/Users/username/node_modules/frisby/lib/frisby.js:493:42) at .<anonymous> (/Users/username/node_modules/frisby/lib/frisby.js:1074:43) at tryOnTimeout (timers.js:232:11) Finished in 0.123 seconds 1 test, 14 assertions, 1 failure, 0 skipped
なんのテストが、どうして失敗したかを出力してくれます。
Usage
Frisby.js API Documentation—Frisby.js
公式ドキュメントはこちら。英語のドキュメントですが、機能も多くないので読んだらだいたい分かる。
APIを叩く
getでデータを送る
var frisby = require('frisby'); frisby.create('getで送るテスト') .get('http://api.zipaddress.net/?zipcode=1000000')
postでデータを送る
var frisby = require('frisby'); frisby.create('postで送るテスト') .post('APIのURL', { "param1": "hogehoge", "request_data": { "data_param1":"1" }, }, { json:true })
{json:true}
を忘れずに。
proxyを使う
GETの場合は.get
の第二引数に、POSTの場合は.post
の第三引数に設定すればいいっぽい。
frisby.create('GETの場合') .get('APIのURL', {'proxy':'http://xxxxxxxxxxxx:port/'})
var frisby = require('frisby'); frisby.create('POSTの場合') .post('APIのURL', { "param1": "hogehoge", "request_data": { "data_param1":"1" }, }, { json:true, 'proxy':'http://xxxxxxxxxxxx:port/'})
APIのレスポンスを確認する
ヘッダー
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .toss();
expectHeader
を使うと完全一致、expectHeaderContains
で部分一致、expectHeaderToMatch
で正規表現が使えます。
レスポンスの中身
expectJSON
を使います。
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectJSON( { 'code': 200, 'data': { 'address': '千代田区', 'city': '千代田区', 'fullAddress': '東京都千代田区', 'pref': '東京都', 'town': '' } } ) .toss();
expectJSONTypes
でレスポンスの型の確認ができます。
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectJSON( { 'code': 200, 'data': { 'address': '千代田区', 'city': '千代田区', 'fullAddress': '東京都千代田区', 'pref': '東京都', 'town': '' } } ) .expectJSONTypes('data', { 'address': String, 'city': String, 'fullAddress': String, 'pref': String, 'town': String }) .toss();
expectJSONTypes
では、期待するレスポンスをそのまま書かずに、第一引数に項目を指定する書き方をしています。expectJSON
でも同じ書き方ができます。
レスポンスタイム
expectMaxResponseTime
でmsで指定します。
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .expectMaxResponseTime(1000) .toss();
テストした内容を出力する
いろいろ出せる。
- inspectJSON:リクエストをJSONで
- inspectBody:リクエストをテキストで
- inspectRequest:HTTPのリクエスト
- inspectStatus:HTTPのステータスコード
- inspectHeaders:HTTPのヘッダー
例えば、JSON形式のリクエストとHTTPのリクエストを出力してみるテストコードはこう。
var frisby = require('frisby'); frisby.create('住所検索') .get('http://api.zipaddress.net/?zipcode=1000000') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectJSON( { 'code': 200, 'data': { 'address': '千代田区', 'city': '千代田区', 'fullAddress': '東京都千代田区', 'pref': '東京都', 'town': '' } } ) .expectJSONTypes('data', { 'address': String, 'city': String, 'fullAddress': String, 'pref': String, 'town': String }) .expectMaxResponseTime(1000) .inspectJSON() .inspectRequest() .toss();
このテストコードでテストを実行すると、このように出力してくれる。
% jasmine-node . { code: 200, data: { pref: '東京都', address: '千代田区', city: '千代田区', town: '', fullAddress: '東京都千代田区' } } { json: false, uri: 'http://api.zipaddress.net/?zipcode=1000000', body: null, method: 'GET', headers: { 'content-type': 'application/x-www-form-urlencoded' }, inspectOnFailure: false, baseUri: '', timeout: 5000 }
ちょっとハマったこと
frisbyは指定したリクエストを小文字に変換してしまうものがあるということ。
叩きたかったAPIで、大文字が入っている独自のheaderを設定する必要があったのですが、frisbyの小文字変換で送りたいヘッダーが送れず。Frisbyのプログラムを見ていたところ小文字変換の処理を見つけて原因が判明。
~/node_modules/frisby/lib/frisby.js
Frisby.prototype.addHeader = function(header, content) { this.current.request.headers[(header+"").toLowerCase()] = content+""; return this; };
仕方ないので、toLowerCase()を外しました。
少し幸せになれそう
frisbyを探すきっかけになったのは、新しく作るAPIのテストパターンが膨大になりそうで、1個1個テスト項目を手動ではやってられないと思ったから。
しかもどこかでテストがこけて、修正して再度テスト、なんてことを考えたら頭が痛くなったのもあった。
テスト目前にこのことに気づくと、テストパターン多い!でも効率な方法を探す時間もない!スケジュールも迫ってる!やるしかない!というパターンに陥るので、早めに気づいてよかったと思っています。
このfrisbyのおかげで、膨大なテストからだいぶ救われそう。