cucumber.jsフレームワークを使ったREST APIの受け入れテスト自動化深堀

Enterprise APIs Advent Calendar 2015の一回目ということでどのネタにしようかいろいろ考えましたが、以前紹介したREST API用テストフレームワークまとめapickli/apickli検証ネタを書きます。

Enterprise APIs Advent Calendar 2015

apickli/apickli

Cucumberフレームワークという、Behaviour-Driven Development (BDD)を実現するフレームワークです。システムテストにおいて受け入れテストに向いている自動化フレームワークです。

apickliは、Cucumberフレームワークをnode.jsで実装したcucumber.jsを活用、自然言語シナリオをもとにREST APIのリクエストに対して期待値のレスポンスをOK/NGをテスト可能。

Given(前提)、When(もし)、Then(ならば)、and(かつ)、but(しかし)を組み合わせでテストシナリオを書いてきます。

事前準備

適当な環境(linux,macなど)でGitHubから環境をとってきます。 node.jsはnvmなどで稼働できるようにしておくとよいです。

では手順にしたがって準備していきます。

node.js install

npm install -g cucumber

apickliをダウンロード

git clone https://github.com/apickli/apickli.git

サンプルプロジェクトの確認

cd apickli/example-project/test
tree
.
└── features
├── fixtures
│   └── requestBody.xml
├── httpbin.feature
└── step_definitions
    ├── apickli-gherkin.js -> ../../../node_modules/apickli/apickli-gherkin.js
    └── httpbin.js

という構成になっていると思います。

package.json編集

cd ..
cat package.json 
{
  "name": "httpbin-test",
  "version": "1.0.0",
  "description": "Integration testing for myapi with httpbin.org",
  "dependencies": {
    "apickli": "latest"
  }
}

上記のように編集します。nameのところは、httpbinを活用したテストなのでこれでよいです。 httpbinとは、REST APIで取り急ぎ、サンプルリクエストに対し、レスポンスを提供してくれる便利な無料APIになっています。

依存関係のモジュールをインストール

npm install

package.jsonの置いてるところで、本コマンドを実施してください。 これで依存関係のモジュールがインストールされます。

サンプルシナリオの宛先確認

cd apickli/example-project/test/features/step_definitions
vi httpbin.js
//中略
this.apickli = new apickli.Apickli('http', 'httpbin.org');

このファイルにテストしたいAPIの宛先を書きます。
まずは、デフォルトのままでよいです。

サンプルシナリオの実行

これでfeatures/httpbin.featureに書かれているシナリオに従って実行されていきます。 とりあえず実行してみましょう。

cd apickli/example-project/test
cucumber-js features/httpbin.feature
テストの標準出力結果一部
Feature:

  Httpbin.org exposes various resources for HTTP request testing
  As Httpbin client I want to verify that all API resources are working as they should

  Scenario: Setting headers in GET request                         # features/httpbin.feature:5
Given I set User-Agent header to apickli                       # ../node_modules/apickli/apickli-gherkin.js:6
When I GET /get                                                # ../node_modules/apickli/apickli-gherkin.js:31
Then response body path $.headers.User-Agent should be apickli # ../node_modules/apickli/apickli-gherkin.js:153

  Scenario: Setting body payload in POST request   # features/httpbin.feature:10
Given I set body to {"key":"hello-world"}      # ../node_modules/apickli/apickli-gherkin.js:11
When I POST to /post                           # ../node_modules/apickli/apickli-gherkin.js:41
Then response body should contain hello-world  # ../node_modules/apickli/apickli-gherkin.js:137
(中略)
Failing scenarios:
features/httpbin.feature:25 # Scenario: Setting body payload from file

24 scenarios (1 failed, 1 ambiguous, 22 passed)
70 steps (1 failed, 2 ambiguous, 2 skipped, 65 passed)
0m13.164s

The following steps have multiple matching definitions:

"I subtract remaining2 from remaining1" matches:
/^I subtract (.*) from (.*)$/ # features/step_definitions/apigw.js:16  
/^I subtract (.*) from (.*)$/ # features/step_definitions/httpbin.js:16

"result should be 1" matches:
/^result should be (\d+)$/ # features/step_definitions/apigw.js:24  
/^result should be (\d+)$/ # features/step_definitions/httpbin.js:24

という感じで、出力されます。一部エラーが出てますね。

テストのオプション

  Usage: cucumber-js [options] [<DIR|FILE[:LINE]>...]

    Options:

-h, --help                     output usage information
-v, --version                  output the version number
-b, --backtrace                show full backtrace for errors
--compiler <EXTENSION:MODULE>  require files with the given EXTENSION after requiring MODULE (repeatable)
-d, --dry-run                  invoke formatters without executing steps
--fail-fast                    abort the run on first failure
-f, --format <TYPE[:PATH]>     specify the output format, optionally supply PATH to redirect formatter output (repeatable)
--no-colors                    disable colors in formatter output
--no-snippets                  hide step definition snippets for pending steps
--no-source                    hide source uris
-p, --profile <NAME>           specify the profile to use (repeatable)
-r, --require <FILE|DIR>       require files before executing features (repeatable)
--snippet-syntax <FILE>        specify a custom snippet syntax
-S, --strict                   fail if there are any undefined or pending steps
-t, --tags <EXPRESSION>        only execute the features or scenarios with tags matching the expression (repeatable)
For more details please visit https://github.com/cucumber/cucumber-js#cli

の感じで、タグ指定なんかを使うとシナリオテストをブロックごとにタグ分けしておくとそこの部分だけテストができます。

cucumber-js features/httpbin.feature --tags @token            
Feature: 

  Httpbin.org exposes various resources for HTTP request testing
  As Httpbin client I want to verify that all API resources are working as they should

  @token
  Scenario: Access token retrieval from response body (authorization code grant, password, client credentials)   # features/httpbin.feature:81
Given I set Token header to token123                                                                         # ../node_modules/apickli/apickli-gherkin.js:6
When I GET /get                                                                                              # ../node_modules/apickli/apickli-gherkin.js:31
Then I store the value of body path $.headers.Token as access token                                          # ../node_modules/apickli/apickli-gherkin.js:169

  @token
  Scenario: Using access token                                                # features/httpbin.feature:87
Given I set bearer token                                                  # ../node_modules/apickli/apickli-gherkin.js:174
When I GET /get                                                           # ../node_modules/apickli/apickli-gherkin.js:31
Then response body path $.headers.Authorization should be Bearer token123 # ../node_modules/apickli/apickli-gherkin.js:153

2 scenarios (2 passed)
6 steps (6 passed)
0m01.104s

こんな感じです。 シナリオの記述の仕方は、Cucumberのfeatureファイルのプラクティスにもありますが、

given (code)     | "前提"                                          
when (code)      | "もし"
then (code)      | "ならば"
and (code)       | "かつ"                                           |
but (code)       | "しかし", "但し", "ただし"

をうまく組み合わせすることで、様々な受け入れテストのパターン分岐のシナリオを記述することができると思います。

apickliで最初から準備されてる便利なところ

基本、test/featuresの*.featureファイルに自然言語にてシナリオを記述し、そのシナリオに従ったコードを、test/features/step_definitions配下にモジュールを記述していきます。

test/features/step_definitions/apickli-gherkin.jsがライブラリ的に最初から用意されているので、

I set body to {"key":"hello-world"}
I POST to /post
response body should be valid json
response header boo should not exist
response body should contain WonderWidgets

などのようなリクエスト、レスポンスの中身チェックが簡単にできるところが、非常にテストに有益と思います。

これで、JenkinsなどのCIツールと組み合わせすれば、APIサーバモジュールリリースに合わせて、シナリオ自動テストができますね。 別途、Jenkins+Docker+cucumber.jsでやってみたいと思います。


REST APIの受け入れテストは、まだまだこれからベストプラクティスが出てくるかと思いますが、他にも良いフレームワークがあれば、実際に使って紹介していきたいと思います。

© NTT Communications Corporation All Rights Reserved.