開発部の三浦です。
今回はDREDDを使用してAPIをテストする方法を書いていこうと思います。
Dreddとは
openapi(swagger)やAPI Blueprintの定義を読み込んでテストを実行できる便利なツールです。
hooksという仕組みを使うと、リクエストに動的に値を組み込んだり、レスポンスで返ってきたtokenをセットし認証な必要なテストを実行したりということが可能になります。
dredd hooks
hooksは様々な言語で書くことができます。
-
GoNode.js (JavaScript)
-
Perl
-
PHP
-
Python
-
Ruby
-
Rust
といった言語に対応しています。自分の得意な言語を選択して記述することができます。
私はJavaScriptをよく触っていたので、今回はJavaScriptで記述していこうと思います。
それではサンプルのファイルを作成し実際にできるところまで進めていきます。
ディレクトリの作成
1 2 3 4 |
mkdir sampleDredd cd sampleDredd |
Dreddのインストール
1 2 3 |
npm install -g dredd |
ローカルサーバーの立ち上げ
今回はjson-serverというモジュールを使用し簡易的に使用できるデータベースを作成します。
詳しく知りたい方は以下の記事を参考にするとかなり詳しく解説してくださっています。
json-serverのインストール
1 2 3 |
npm install --save-dev json-server |
db.jsonファイルが作成されますのでそちらを編集します。
1 2 3 4 5 6 7 8 9 10 11 |
// db.json { "users": [ { "id": 1, "name": "user01" } ] } |
openapi.ymlの作成
1 2 3 |
touch openapi.yml |
openapiファイルには以下を記述します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// openapi.yml openapi: "3.0.0" info: title: sampleapi description: 開発のサンプルです version: 1.0.0 servers: - url: http://localhost:3000 description: ローカルの開発用サーバー paths: /users: get: summary: Get all users. description: Returns an array of User model responses: '200': description: A JSON array of User model content: application/json; charset=utf-8: schema: type: array items: type: object properties: id: type: integer name: type: string post: summary: Create a new User description: Create a new User requestBody: description: user to create content: application/json: schema: # POSTするオブジェクト $ref: '#/components/schemas/User' example: id: name: post_user responses: '201': description: CREATED components: schemas: # スキーマオブジェクトの定義 User: # モデル名 type: object required: # 必須フィールド - id properties: id: type: integer name: type: string |
hookファイルの作成
1 2 3 |
touch hooks.js |
1 2 3 4 5 6 7 8 9 |
// hooks.js let hooks = require('hooks'); let stash = {}; // transctionを見る hooks.beforeAll(function (transactions) { hooks.log(transactions); }); |
ファイルの準備がととのいましたので
以下のコマンドでlocalhost3000に簡易サーバーを立ち上げます
1 2 3 |
json-server --watch db.json |

もう一つのターミナルでdreddのテストを行います。
1 2 3 |
dredd ./openapi.yml http://localhost:3000 --hookfiles="./hooks.js" |

テストが二つ行われ2つとも通ったことが記述されています。
ローカルサーバーにも
レスポンスがきていることが確認できます。
Hookとは
それではここからhookについて解説していきます。
公式によると
Dreddは、各テストステップの前後に実行される任意のコードのブロックであるフックをサポートします。概念は、XUnitの機能、Cucumberフック、またはGitフックに似ています。フックは通常、次の目的で使用されます。
各テストステップというのは
今回で言うと
- /users get
- /users post
この2つのことを指します。
このテストステップのことをdreddはTransactionと呼んでいます。
このTransactionというものの前後に処理を組み込めるというのがHookです。
jsでは以下のものが用意されていますので確認して見てください。
Hookの使い方
では実際に二つ目のTransactionである/users postを指定し、
別の値を入れてpostしてみようと思います。
Tranactionの指定
1 2 3 4 5 |
hooks.before("transactionの名前", function (transaction) { hooks.log("before"); }); |
hooks関数は引数にTranactionの名前をとります。
Tranactionの名前の取り方は --namesオプションで確認することができます。
1 2 3 |
dredd ./openapi.yml http://localhost:3000 --names |
このinfo:部分がtransactionの名前です。この部分を指定することで処理を挟み込むことができます。
hooks.log
ターミナルにログを出力してくれます。console.logと使い方はほぼ一緒ですが引数は一つしか取れません。
hooks.jsの編集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
let hooks = require('hooks'); let stash = {} // POST (201) /usersの前 hooks.before("/users > Create a new User > 201", function (transaction) { hooks.log("before POST (201) /users"); // openapi定義の値を書き換える let requestBody = { id: null, name: "hooksEditPostUserName", info: "hoge" } transaction.request.body = JSON.stringify(requestBody); // 送る値をlogに出力 hooks.log(transaction.request.body) }); // POST (201) /usersの後 hooks.after("/users > Create a new User > 201", function (transaction) { hooks.log("after POST (201) /users"); // レスポンスの値の確認 hooks.log(transaction.real.body) }); |
dreddを起動します
1 2 3 |
dredd ./openapi.yml http://localhost:3000 --hookfiles="./hooks.js" |
データベースの値を確認してみると
書き換えた値が入っていることが確認できました。
stashの使い方
dreddが提供しているものとしてstashを利用してテストステップ間でデータを受け渡すことができます。
例えばログイン後の認証情報を使ってテストを実行したい場合などに使用できます。
ブログでは使用していませんが例えばuser/loginの処理の後に
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
let hooks = require('hooks'); let stash = {} // ログイン処理 hooks.after("User > ログイン > /user/login", function (transaction) { let requestBody = { email: 'test@sample.com', password: "password" } transaction.request.body = JSON.stringify(requestBody); stash['token'] = JSON.parse(transaction.real.body)['token'] }); // 以下のテストステップで認証情報をヘッダーに組み込む処理 hooks.beforeEach(function (transaction) { if(stash['token'] != undefined){ if (transaction.expected.statusCode != "401"){ transaction.request.headers['Authorization'] = "Bearer " + stash['token']; }; }; }); |
上記の処理を付け加えると認証情報を取得し以降ヘッダーに組み込んでくれます。
axiosとの連携
他にも、例えばTwitterのいいね機能などのテストでは、
-
テスト前に現在のいいね数を取得
-
いいねのレスポンスを送信
-
いいねがつけれた事と、実際にいいね数が増えたかまで確認してテストを通す
というテストまで行いたいです。
その時は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
let hooks = require('hooks'); let stash = {} const axiosBase = require('axios'); const axios = axiosBase.create({ baseURL: 'http://localhost:3000', // バックエンドB のURL:port を指定する headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, responseType: 'json' }); hooks.before('/post/like > いいねを実施 > 200 > application/json', async function (transaction, done) { let body = JSON.parse(transaction.request.body) // リクエストで送る値 let requestBody = { "user_id": 1, } // いいね数を取得しstashに保存する await axios.post('/post/like_count', requestBody, {headers:{'Authorization' : "Bearer " + stash['token']}}) // 認証情報が必須な場合は3つ目の引数に認証情報を含める .then(function (response) { like_count = response.data.like_count // stashにpost_like_countとして値を保持させておく stash['post_like_count'] = like_count }) done(); }); hooks.after('/post/like > いいねを実施 > 200 > application/json > 200 > application/json', async function (transaction, done) { let body = JSON.parse(transaction.request.body) // リクエストで送る値 let requestBody = { "user_id": 1, } // いいね数を取得し評価する await axios.post('/post/like_count', requestBody, {headers:{'Authorization' : "Bearer " + stash['token']}}) .then(function (response) { like_count = response.data.like_count // 保持させていたリクエスト前のものと今取得したものを比較 if (stash['post_like_count'] > like_count ){ // dreddが用意してくれているtransaction.failで強制的にテストを失敗させる transaction.fail = '投稿へのいいね後にいいね数が増加していません' } }) done(); }); |
冗長にはなってしましますが、テスト前にaxiosで通信を行い、差異を確認という処理をすることができます。
以上になります。お読みいただきありがとうございました。
このブログは株式会社CoLabMixによる技術ブログです。
GCP、AWSなどでのインフラ構築・運用や、クローリング・分析・検索などを主体とした開発を行なっています。
Ruby on RailsやDjango、Pythonなどの開発依頼などお気軽にお声がけください。
開発パートナーを増やしたいという企業と積極的に繋がっていきたいです。