スポンサーリンク
私が担当しているサービスではAPIを提供しています。
そこで最近思うのは、APIを公開する場合、使い方やパラメータを記載するリファレンスが非常に重要になるということです。
そんなAPIリファレンスには専用のアプリケーションであるswaggerがあります。
これはAPIのリクエスト/レスポンスのパラメータを詳細に定義でき、場合によってはswagger-ui上からテスト実行することもできる非常に便利なツールです。
そんなswaggerの実体はjson or yamlで書かれた定義ファイルなのですが、これを使って試験を自動で行うツールがあるんじゃないのか?と思い探したところ見つけました。
今回はそんなテストツールである dredd の使い方について書きたいと思います。
swagger等のAPI仕様書を元にAPIのテストを自動で実行してくれるツールです。
具体的には、仕様書に記載されたAPIのエンドポイントに対して、定義されたリクエストを送ると定義されたレスポンスパラメータが正しく返ってくるかを確認するツールです。
詳細はこちらに書いてありますが、今回は抜粋してインストールからテスト実行までのやり方をご説明します。
私もそれほど詳しく知っているわけではないですが、、、
といったところが便利だと思いました。
今回は以下の環境でテストします。
ということで、早速macにインストールです。
dreddはnode.jsで動くとのことなので、まずはnode.jsをインストールします。
brewコマンドで一発です。
~$ brew install node
Updating Homebrew...
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#donations
==> Migrating tap caskroom/cask to homebrew/cask...
Changing remote from https://github.com/caskroom/homebrew-cask to https://github.com/Homebrew/homebrew-cask...
Moving /usr/local/Homebrew/Library/Taps/caskroom/homebrew-cask to /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask...
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/core, homebrew/services, homebrew/cask).
==> Installing dependencies for node: icu4c
==> Installing node dependency: icu4c
==> Downloading https://homebrew.bintray.com/bottles/icu4c-62.1.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring icu4c-62.1.high_sierra.bottle.tar.gz
==> Caveats
icu4c is keg-only, which means it was not symlinked into /usr/local,
because macOS provides libicucore.dylib (but nothing else).
If you need to have icu4c first in your PATH run:
echo 'export PATH="/usr/local/opt/icu4c/bin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/opt/icu4c/sbin:$PATH"' >> ~/.bash_profile
For compilers to find icu4c you may need to set:
export LDFLAGS="-L/usr/local/opt/icu4c/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c/include"
For pkg-config to find icu4c you may need to set:
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
==> Summary
🍺 /usr/local/Cellar/icu4c/62.1: 250 files, 67.3MB
==> Installing node
==> Downloading https://homebrew.bintray.com/bottles/node-10.10.0.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring node-10.10.0.high_sierra.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
==> Summary
🍺 /usr/local/Cellar/node/10.10.0: 3,940 files, 49.3MB
==> Caveats
==> icu4c
icu4c is keg-only, which means it was not symlinked into /usr/local,
because macOS provides libicucore.dylib (but nothing else).
If you need to have icu4c first in your PATH run:
echo 'export PATH="/usr/local/opt/icu4c/bin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/opt/icu4c/sbin:$PATH"' >> ~/.bash_profile
For compilers to find icu4c you may need to set:
export LDFLAGS="-L/usr/local/opt/icu4c/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c/include"
For pkg-config to find icu4c you may need to set:
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
==> node
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
以下のようなレスポンスが得られればOKです。
~$ node --version
v10.10.0
次に本題のdreddをインストールします。
こちらはnpmコマンドで一発です
~$ npm install -g dredd
npm WARN deprecated json-schema-faker@0.5.0-rc13: Broken not support
npm WARN deprecated nomnom@1.5.2: Package no longer supported. Contact support@npmjs.com for more info.
/usr/local/bin/dredd -> /usr/local/lib/node_modules/dredd/bin/dredd
> protagonist@1.6.8 install /usr/local/lib/node_modules/dredd/node_modules/protagonist
> node-gyp rebuild
CXX(target) Release/obj.target/libmarkdownparser/drafter/ext/snowcrash/ext/markdown-parser/src/ByteBuffer.o
CXX(target) Release/obj.target/libmarkdownparser/drafter/ext/snowcrash/ext/markdown-parser/src/MarkdownNode.o
〜〜〜〜中略〜〜〜〜〜
CXX(target) Release/obj.target/protagonist/src/options_parser.o
../src/options_parser.cc:30:33: warning: 'Utf8Value' is deprecated [-Wdeprecated-declarations]
const String::Utf8Value strKey(key);
^
/Users/ken/.node-gyp/10.10.0/include/node/v8.h:2887:5: note: 'Utf8Value' has been explicitly marked deprecated here
V8_DEPRECATED("Use Isolate version",
^
/Users/ken/.node-gyp/10.10.0/include/node/v8config.h:327:29: note: expanded from macro 'V8_DEPRECATED'
declarator __attribute__((deprecated))
^
../src/options_parser.cc:49:41: warning: 'Utf8Value' is deprecated [-Wdeprecated-declarations]
const String::Utf8Value strValue(value);
^
/Users/ken/.node-gyp/10.10.0/include/node/v8.h:2887:5: note: 'Utf8Value' has been explicitly marked deprecated here
V8_DEPRECATED("Use Isolate version",
^
/Users/ken/.node-gyp/10.10.0/include/node/v8config.h:327:29: note: expanded from macro 'V8_DEPRECATED'
declarator __attribute__((deprecated))
^
2 warnings generated.
CXX(target) Release/obj.target/protagonist/src/parse_async.o
../src/parse_async.cc:61:23: warning: 'Utf8Value' is deprecated [-Wdeprecated-declarations]
String::Utf8Value sourceData(info[0]->ToString());
^
/Users/ken/.node-gyp/10.10.0/include/node/v8.h:2887:5: note: 'Utf8Value' has been explicitly marked deprecated here
V8_DEPRECATED("Use Isolate version",
^
/Users/ken/.node-gyp/10.10.0/include/node/v8config.h:327:29: note: expanded from macro 'V8_DEPRECATED'
declarator __attribute__((deprecated))
^
../src/parse_async.cc:128:14: error: no matching constructor for initialization of 'v8::TryCatch'
TryCatch try_catch;
^
/Users/ken/.node-gyp/10.10.0/include/node/v8.h:8530:12: note: candidate constructor not viable: requires single argument 'isolate', but no arguments were provided
explicit TryCatch(Isolate* isolate);
^
/Users/ken/.node-gyp/10.10.0/include/node/v8.h:8650:3: note: candidate constructor not viable: requires 1 argument, but 0 were provided
TryCatch(const TryCatch&) = delete;
^
1 warning and 1 error generated.
make: *** [Release/obj.target/protagonist/src/parse_async.o] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack at ChildProcess.emit (events.js:182:13)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:240:12)
gyp ERR! System Darwin 17.7.0
gyp ERR! command "/usr/local/Cellar/node/10.10.0/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /usr/local/lib/node_modules/dredd/node_modules/protagonist
gyp ERR! node -v v10.10.0
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
> jsonpath@1.0.0 postinstall /usr/local/lib/node_modules/dredd/node_modules/jsonpath
> node lib/aesprim.js > generated/aesprim-browser.js
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: protagonist@1.6.8 (node_modules/dredd/node_modules/protagonist):
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: protagonist@1.6.8 install: `node-gyp rebuild`
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: Exit status 1
+ dredd@5.1.11
added 253 packages from 259 contributors in 137.906s
なんかエラーが出ていますが、下記のようにレスポンスが返って来ればOKのようです
~$ dredd --version
dredd v5.1.11 (Darwin 17.7.0; x64)
次にswaggerの用意です。
私の場合は元々実際のサービスで使っているものがありますが、ここでは適当なサンプルを用意します。
# sample_swagger.yaml
swagger: "2.0"
info:
version: "1.0"
title: Example API
license:
name: MIT
host: "APIサーバのIP"
basePath: /
schemes:
- http
paths:
/blogs:
post:
produces:
- application/json; charset=utf-8
parameters:
- schema:
$ref: '#/definitions/blog_post'
description: ''
required: true
name: 'blogs'
in: body
responses:
200:
description: ""
schema:
type: object
$ref: '#/definitions/blog_response'
/blogs/1:
get:
produces:
- application/json; charset=utf-8
responses:
200:
description: ""
schema:
type: object
$ref: '#/definitions/blog_response'
definitions:
blog_response:
type: object
properties:
id:
type: integer
example: 1
title:
type: string
example: "hello blogs"
content:
type: string
example: "this is test post"
created_at:
type: string
example: '2018-08-01 00:00:00'
updated_at:
type: string
example: '2018-08-01 00:00:00'
blog_post:
type: object
required:
- title
- content
properties:
title:
type: string
example: "hello blogs"
content:
type: string
example: "this is test post"
長くなるのでここでは割愛しますが、rails等で割と簡単に構築できます。
ここまでできれば準備OKです。実際にdreddを実行してみます。
$ dredd sample_swagger.yaml http://"APIサーバのIP" -d
以下のようなレスポンスが返って来ればテスト成功です。
info: Beginning Dredd testing...
pass: POST (200) /blogs duration: 70ms
request:
method: POST
uri: /blogs
headers:
Accept: application/json; charset=utf-8
User-Agent: Dredd/5.1.11 (Darwin 17.7.0; x64)
Content-Length: 0
body:
expected:
headers:
Content-Type: application/json; charset=utf-8
body:
{
"id": -80569487,
"title": "eiusmod dolor nulla veniam",
"content": "commodo reprehenderit cillum exercitation",
"created_at": "enim",
"updated_at": "eiusmod et fugiat"
}
statusCode: 200
bodySchema: {"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"content":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"}}}
actual:
statusCode: 200
headers:
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
content-type: application/json; charset=utf-8
etag: W/"02c045b5a38b1e9a01bf15ab6d48d526"
cache-control: max-age=0, private, must-revalidate
x-request-id: 53761b97-9be6-41ed-b73a-3232d225ccf4
x-runtime: 0.011221
connection: close
transfer-encoding: chunked
body:
{
"result": "success"
}
pass: GET (200) /blogs/1 duration: 36ms
request:
method: GET
uri: /blogs/1
headers:
Accept: application/json; charset=utf-8
User-Agent: Dredd/5.1.11 (Darwin 17.7.0; x64)
Content-Length: 0
body:
expected:
headers:
Content-Type: application/json; charset=utf-8
body:
{
"id": -75997955,
"title": "eiusmod labore",
"content": "est magna qui id",
"created_at": "qui veniam",
"updated_at": "tempor occaecat labore"
}
statusCode: 200
bodySchema: {"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"content":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"}}}
actual:
statusCode: 200
headers:
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
content-type: application/json; charset=utf-8
etag: W/"6c25d825d92fb03d7d50459af421d95a"
cache-control: max-age=0, private, must-revalidate
x-request-id: 0e7491ba-aa1c-47bf-9bdf-31ea267b23ac
x-runtime: 0.007006
connection: close
transfer-encoding: chunked
body:
{
"id": 1,
"title": "test1",
"content": "this is test dayo",
"created_at": "2017-06-01T01:15:26.223Z",
"updated_at": "2017-06-01T01:15:26.223Z"
}
complete: 2 passing, 0 failing, 0 errors, 0 skipped, 2 total
complete: Tests took 112ms
末尾に"2 passing"とありますので2つの試験が成功したことがわかります。
とりあえずdreddを使ってテストをするというところまでできました。
次回はより実践的な使い方(トークンを取得するなど)のやり方を描きたいと思います。
スポンサーリンク