API Blueprintで書いたAPI仕様書をDreddを使ってCircleCIでテストしてみた
今回、あるプロジェクトで API Blueprint を使って、API仕様書を書くことになったので、 API Blueprint
について調べていたのだが、 Dredd というAPIのテストフレームワークが便利そうだったので、 CircleCI
で簡単なテストするところまで触ってみた。
Dredd
は、 apiary.io
が提供しているAPIのテストフレームワークで、 API Blueprint
だけではなく、 Swagger にも対応している。
試してみた
API作成
まずは Golang
で非常に簡単な API
を作成。
/message
にアクセスすると、 Hello World!!
が返るだけ。
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/message", index)
p := port()
fmt.Println("Listening on Port", p)
http.ListenAndServe(p, nil)
}
func port() string {
port := os.Getenv("PORT")
if len(port) == 0 {
port = "8080"
}
return ":" + port
}
func index(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello World!!")
}
go build
をして、サーバーを起動。
$ ./simple-go-server
Listening on Port :8080
別のコンソールから、 curl
を実行してちゃんとレスポンスが返ってくることを確認する。
$ curl localhost:8080/message
Hello World!!
API Blueprint でドキュメント作成
API Blueprint
形式のドキュメント doc.apib
を作成する。
FORMAT: 1A
# GET /message
+ Response 200 (text/plain; charset=utf-8)
Hello World!!
Dredd のインストール
Node.js
がインストール済みの環境で、下記コマンドを実行して Dredd
をインストールする。
$ npm install -g dredd
$ dredd --version
Dredd 設定ファイル作成
dredd init
を実行することで対話形式で設定ファイル dredd.yml
が作成できる。
$ dredd init
? Location of the API description document doc.apib
? Command to start API backend server e.g. (bundle exec rails server) ./simple-go-server
? URL of tested API endpoint localhost:8080
? Programming language of hooks go
? Do you want to use Apiary test inspector? No
? Dredd is best served with Continuous Integration. Create CircleCI config for Dredd? No
Configuration saved to dredd.yml
Install hooks handler and run Dredd test with:
$ go get github.com/snikch/goodman/cmd/goodman
$ dredd
- APIのドキュメントのファイル名
doc.apib
を入力- API サーバー起動コマンド
./simple-go-server
を入力- APIのエンドポイント
localhost:8080
を入力- Hook を利用する際の言語
go
を下記のように出てくる選択肢より選択
? Programming language of hooks
ruby
python
nodejs
php
perl
❯ go
rust
Apiary test inspector
を利用するかn
を入力CircleCI
の設定ファイルを作成するか- 現時点では、
CircleCI 2.0
には対応しておらず、1.0
の設定ファイルcircle.yml
が作られるため、n
を選択
生成された dredd.yml
の設定内容はこんな感じになっている。
dry-run: null
hookfiles: null
language: go
sandbox: false
server: ./simple-go-server
server-wait: 3
init: false
custom: {}
names: false
only: []
reporter: []
output: []
header: []
sorted: false
user: null
inline-errors: false
details: false
method: []
color: true
level: info
timestamp: false
silent: false
path: []
hooks-worker-timeout: 5000
hooks-worker-connect-timeout: 1500
hooks-worker-connect-retry: 500
hooks-worker-after-connect-wait: 100
hooks-worker-term-timeout: 5000
hooks-worker-term-retry: 500
hooks-worker-handler-host: 127.0.0.1
hooks-worker-handler-port: 61321
config: ./dredd.yml
blueprint: doc.apib
endpoint: 'localhost:8080'
Dredd 実行
対話の最後に指示が出ており、その指示に従ってコマンドを実行する。
$ go get github.com/snikch/goodman/cmd/goodman
を実行して、 github.com/snikch/goodman/cmd/goodman
をインストールした後に、 dredd
コマンドを実行。
すると、先ほど作成した設定ファイル ./dredd.yml
を見つけて、その設定に従って、APIサーバーを起動。
エンドポイントに対してアクセスをして、ドキュメント通りの期待したレスポンスが返ってくるかのテストを行ってくれる。
$ dredd
info: Configuration './dredd.yml' found, ignoring other arguments.
info: Starting backend server process with command: ./simple-go-server
info: Waiting 3 seconds for backend server process to start
Listening on Port :8080
info: Beginning Dredd testing...
pass: GET (200) /message duration: 29ms
complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total
complete: Tests took 34ms
info: Backend server process exited
正常にテストが通った。
ちなみに、 doc.apib
を下記のように、 Hello
を Good Morning
に修正して、
FORMAT: 1A
# GET /message
+ Response 200 (text/plain; charset=utf-8)
Good Morning World!!
テストを再度実行してみると……
$ dredd
info: Configuration './dredd.yml' found, ignoring other arguments.
info: Starting backend server process with command: ./simple-go-server
info: Waiting 3 seconds for backend server process to start
Listening on Port :8080
info: Beginning Dredd testing...
fail: GET (200) /message duration: 30ms
info: Displaying failed tests...
fail: GET (200) /message duration: 30ms
fail: body: Real and expected data does not match.
request:
method: GET
uri: /message
headers:
User-Agent: Dredd/4.9.2 (Darwin 17.3.0; x64)
Content-Length: 0
body:
expected:
headers:
Content-Type: text/plain; charset=utf-8
body:
Good Morning World!!
statusCode: 200
actual:
statusCode: 200
headers:
date: Tue, 16 Jan 2018 08:22:59 GMT
content-length: 14
content-type: text/plain; charset=utf-8
connection: close
body:
Hello World!!
complete: 0 passing, 1 failing, 0 errors, 0 skipped, 1 total
complete: Tests took 34ms
info: Backend server process exited
ちゃんと fail: body: Real and expected data does not match.
と表示されていて、 body
が一致していないっていう内容で Fail
した。
╭( ・ㅂ・)و ̑̑ グッ !
Apiary test inspector
ちなみに、設定ファイル作成時に、 Apiary test inspector
を利用する設定にすると、 Apiary API key
を聞かれる。
何も入力せずにエンターキーを押すと、テスト結果が https://app.apiary.io
アップロードされ、パブリックにアクセスできるようになり、WEB上で確認できる。
テスト実行した最後に、下記のようにURLが表示されて、
complete: See results in Apiary at: https://app.apiary.io/public/tests/run/eb7093e0-565e-45e4-82f1-de14fda0cf72
URLにアクセスしてみると、下記のような画面となり、テストの結果が確認できる。
テスト結果は、24時間保持される。
Hook について
あるエンドポイントにアクセスする前に、テスト用のダミーデータを作成するとか、前のテストで作られたデータを次のテスト実行前に削除したい場合などには、 Hook
を利用することができる。
dredd init
を実行した際に、選択肢として表示されたように、 Hook
で使用できる言語は、
- Go
- JavaScript (Sandboxed)
- Node.js
- Perl
- PHP
- Python
- Ruby
となっている。
【参考URL】
Hook Scripts — Languages
Hook
で利用できる種類はドキュメントを参考にしていただきたいが、下記の種類を指定できる。
- beforeAll
- beforeEach
- before
- beforeEachValidation
- beforeValidation
- after
- afterEach
- afterAll
【参考URL】
Hook Scripts — Types of Hooks
Hook 作成
ドキュメントの例を参考に、ほぼそのままではあるが test/hooks.go
として下記のような Hook
を作成した。
/message
へアクセスする前に、コンソール上にメッセージを表示するだけ……。
package main
import (
"fmt"
"github.com/snikch/goodman/hooks"
trans "github.com/snikch/goodman/transaction"
)
func main() {
h := hooks.NewHooks()
server := hooks.NewServer(hooks.NewHooksRunner(h))
h.Before("/message > GET", func(t *trans.Transaction) {
fmt.Println("-------------------")
fmt.Println("before modification")
fmt.Println("-------------------")
})
server.Serve()
defer server.Listener.Close()
}
作成した上で、 go build
を行う。
$ go build -o hooks test/hooks.go
hooks
ファイルができるので、 dredd.yml
で、
hookfiles: null
となっていた箇所を下記のように変更する。
hookfiles: hooks
再度テストを実行してみると、
$ dredd
info: Configuration './dredd.yml' found, ignoring other arguments.
info: Starting backend server process with command: ./simple-go-server
info: Waiting 3 seconds for backend server process to start
Listening on Port :8080
info: Beginning Dredd testing...
info: Found Hookfiles: 0=/Users/eugenesasa/work/src/github.com/mmmsasaki/simple-go-server/hooks
info: Spawning 'go' hooks handler process.
info: Hooks handler stdout: Sending to channel
Completed
info: Hooks handler stderr: 2018/01/18 07:42:27 Starting hooks server
info: Hooks handler stdout: Starting
info: Hooks handler stdout: Accepting connection
info: Successfully connected to hooks handler. Waiting 0.1s to start testing.
info: Hooks handler stdout: -------------------
before modification
-------------------
pass: GET (200) /message duration: 53ms
info: Hooks handler stderr: 2018/01/18 07:42:28 Shutting down hooks servers
complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total
complete: Tests took 5653ms
info: Backend server process exited
/message
へアクセスする前に、メッセージが表示されており、 hooks
が実行されているのが見えるかと思う。
info: Hooks handler stdout: -------------------
before modification
-------------------
以下の Express.js
のサンプルでは、データのフィクスチャを読み込んだりしているので、こちらも参考に。
【参考URL】
dredd-example/hooks.js
CircleCI
これを、 CircleCI
でも実行することで、継続的に実装した機能と、ドキュメントの乖離がないかということがチェックできる。
.circleci/config.yml
は下記のようにした。
version: 2
jobs:
build:
docker:
- image: circleci/golang:latest
working_directory: /go/src/github.com/mmmsasaki/simple-go-server
steps:
- checkout
- run: curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
- run: sudo apt-get install -y nodejs
- run: go version
- run: node --version
- run: npm --version
- restore_cache:
name: restore node_modules cache
key: dredd
- run:
name: npm install dredd
command: npm install dredd
- save_cache:
name: save node_modules cache
key: dredd
paths:
- node_modules
- run: go build
- run: go get github.com/snikch/goodman/cmd/goodman
- run: go build -o hooks ./test/hooks.go
- run: ./node_modules/dredd/bin/dredd
ちゃんとテストが通っていることが確認できた。
キャッシュが効いていれば、今回の簡単なテストで35秒ぐらいで終わった。
まとめ
API仕様書の管理の課題として、
ドキュメント起票者のミスや、変更仕様の反映漏れなどにより、ドキュメント仕様と実際のAPIが乖離してしまい、使えないドキュメント化してしまう
ということが往々にしてあることだと思うが、 API Blueprint
と Dredd
を使い、 CircleCI
で、ドキュメントとAPIの間で乖離がないか、というチェックを継続的に行えば、この問題は解決できそうな気もしている。
また、実際にサーバーを起動して、アクセスしてレスポンスをテストするので、 テストコードを書く工数ももしかしたら削減できるかもしれないな、と少し期待している。
今回の各種ファイルは下記リポジトリに置いてある。
mmmsasaki/simple-go-server
参考
公式ページ
API Blueprint
Dredd — HTTP API Testing Framework
こんなに使える!今どきのAPIドキュメンテーションツール
API Blueprintで書いたWeb APIをdreddでテストする - Qiita
Apiドキュメンテーションツールを使いこなす【api blueprint編】
API Blueprint で API 仕様書を書いて、配布用の HTML を自動生成する方法 | GMOアドパートナーズグループ TECH BLOG byGMO
API BlueprintでWeb APIのドキュメントを生成する - Qiita