マイクロサービスにおけるmountebankを用いた仮想サービスのテスト入門

mountebankはマイクロサービスを作る際に、仮想サービスによるテストを可能にするツールでThoughtWorks社によって開発されました。まだ実際にはできていない別サービスのREST APIの機能を用いて、今作っているサービスのテストをしたいときに、mountebankによって仮想サービスとして扱うことで、テストを行うことができます。([mountebankの公式サイト](http://www.mbtest.org))

オライリーのマイクロサービスの本（[マイクロサービスアーキテクチャ](https://www.oreilly.co.jp/books/9784873117607/)）でも紹介されていたので、以前から試してみたいと思っていました。

今回はmountebankのインストールから基本的な使い方までを試してみました。

## mountebankで作成した仮想サービスによるテストの仕組み

mountebankで作った仮想サービスを用いて、テスト対象のサービスをテストするときのイメージ図が以下になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/156927/c6bca38a-d3da-ca30-ecca-251432c21ec1.png" width=70%>

公式のmountebankのドキュメントの中で仮想サービスに当たるものはImposter(偽物)と呼ばれていますので、図もそれに対応しています。mountebankにより仮想サービスを作る方法は大きく分けて2つになります。

- curlなどを用いて仮想サービスの設定をREST APIで送って作る
- 仮想サービスの設定ファイルを読み込ませて作る

mountebankはREST APIか設定ファイルのいずれかで仮想サービスの設定を受け取ると、Imposterを作成します。このImposterは設定で決められたリクエストが来た時に同じく決められたレスポンスを返します。サービスを作る前の段階でのサービス間のテストを可能にします。

mountebankはプロトコルとしてHTTPだけでなく、以下のプロトコルが使用可能です。

- HTTP
- HTTPS
- TCP
- SMTP

## mountbankのインストール
mountebankのインストールにはいくつか選択肢があるようですが、今回はnpmを使ってインストールします。（[インストール選択肢](http://www.mbtest.org/docs/install)）node.jsのバージョンはv4以上がインストール要件とのことです。

npmのインストールでは以下のコマンドを実行します。npmを使ったインストール方法は、公式の[Getting Started](http://www.mbtest.org/docs/gettingStarted)に書いてありました。

```bash
$ npm install -g mountebank
```

mountebankのツール自体のインストールはこれだけで終わりです。

## mountebankの起動

mountebankの起動は以下のコマンドを実行します。

```bash
$ mb
```

実行すると以下のような表示がでて、デフォルト設定ですと、localhostの2525番ポートにmountebankが起動していることがわかります。この2525番ポートに対して、curlコマンドでREST APIを実行することで、仮想サービスに相当するImposterを作成していきます。

```Output
info: [mb:2525] mountebank v1.14.0 now taking orders - point your browser to http://localhost:2525 for help
```

ちなみに試しに http://localhost:2525 にブラウザでアクセスした所、[mountebankの公式サイト](http://www.mbtest.org)のトップ画面と同じページが表示されました。

## Imposter（仮想サービス）の作成

設定ファイルによる作成も可能ですが、今回はcurlコマンドを使ったREST APIで作成していきます。

仮想サービスとなるImposterを作成するため、curlコマンドで2525番ポートに以下のように実行します。作成するImposter（仮想サービス）のポート番号は3000、プロトコルはHTTPを指定しています。また、responses以下で、ImposterにHTTPリクエストしたときに、返ってくるレスポンスの設定をしています。今回は作成するサービスは、Product CatalogサービスとしてHTTPリクエストがあった時に製品情報をレスポンスとして返すサービスとなります。

```bash
$ curl -X POST http://localhost:2525/imposters --data '{
  "port": 3000,
  "protocol": "http",
  "stubs": [{
    "responses": [{
      "is": {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": {
          "products": [
            {
              "id": "2599b7f4",
              "name": "The Midas Dogbowl",
              "description": "Pure gold"
            },
            {
              "id": "e1977c9e",
              "name": "Fishtank Amore",
              "description": "Show your fish some love"
            }
          ],
          "_links": {
            "next": "/products?page=2&itemsPerPage=2"
          }
        }
      }
    }]
  }]
}'
```

Imposterの作成で返ってくるレスポンスは以下のようになります。curlコマンドのbodyで設定したレスポンス情報が正しく設定されていることがわかります。

```json
{
 "protocol": "http",
  "port": 3000,
  "numberOfRequests": 0,
  "requests": [],
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "statusCode": 200,
            "headers": {
              "Content-Type": "application/json"
            },
            "body": {
              "products": [
                {
                  "id": "2599b7f4",
                  "name": "The Midas Dogbowl",
                  "description": "Pure gold"
                },
                {
                  "id": "e1977c9e",
                  "name": "Fishtank Amore",
                  "description": "Show your fish some love"
                }
              ],
              "_links": {
                "next": "/products?page=2&itemsPerPage=2"
              }
            }
          }
        }
      ]
    }
  ],
  "_links": {
    "self": {
      "href": "http://localhost:2525/imposters/3000"
    }
  }
}

```

また、mbコマンドを実行したディレクトリにmb.logというログファイルができていて、その中身を見ると以下のようなログがあります。HTTPの3000ポートにImposterが作られたことが示されていて、[http:3000]のようにかっこの中のポート番号を見ることで、複数作成したときでもどのImposterのログなのかということがわかります。

```json
{"level":"info","message":"[http:3000] Open for business...","timestamp":"2018-03-08T10:40:49.193Z"}
```

これで簡単なImposterが作成できたので、curlでリクエストを飛ばして確認してみます。レスポンスのヘッダーを表示させるため、-iオプションを付けて実行します。

```bash
$ curl -i -H'Accept: application/json' http://localhost:3000/products
```

```Output
TP/1.1 200 OK
Content-Type: application/json
Connection: close
Date: Thu, 08 Mar 2018 11:00:19 GMT
Transfer-Encoding: chunked

{
    "products": [
        {
            "id": "2599b7f4",
            "name": "The Midas Dogbowl",
            "description": "Pure gold"
        },
        {
            "id": "e1977c9e",
            "name": "Fishtank Amore",
            "description": "Show your fish some love"
        }
    ],
    "_links": {
        "next": "/products?page=2&itemsPerPage=2"
    }
}
```

正しく設定したレスポンスが返ってきていますね。これで、まだできていないサービスを仮想サービスと見たててテストをすることができます。

## 作成したImposter情報の取得

- 作成した1つのImposterの情報を取得する場合 (GET /imposters/:port)

```bash
$ curl -X GET http://localhost:2525/imposters/3000
```

- 作成した全てのImposterの情報を取得する場合

```bash
$ curl -X GET http://localhost:2525/imposters
```

## Imposter(仮想サービス)の削除

- 作成した1つのImposterを削除する場合 (DELETE /imposters/:port)

```bash
$ curl -X DELETE http://localhost:2525/imposters/3000
```

- 作成した全てのImposterを削除する場合

```bash
$ curl -X DELETE http://localhost:2525/imposters
```

## mountebankの情報の確認

mountebankが起動した状態で以下のそれぞれのURLにブラウザでアクセスすると色々な情報が確認できます。

- 作成したImposter情報：http://localhost:2525/imposters
- mountebankの設定情報：http://localhost:2525/config
- ログ情報：http://localhost:2525/logs

## 条件マッチによるImposterのレスポンス

HTTPリクエストのパラメータが、条件にマッチした時にレスポンスを返すようなImposterも作成することができます。以下のコマンドを実行してImposterを作成します。(3000番ポートを使用したImposterがある場合は削除してから実行)

```bash
$ curl -X POST http://localhost:2525/imposters --data '{
  "port": 3000,
  "protocol": "http",
  "stubs": [
    {
      "predicates": [{ "equals": { "query": { "page": "2" } } }],
      "responses": [{
        "is": {
          "statusCode": 200,
          "headers": { "Content-Type": "application/json" },
          "body": { "products": [] }
        }
      }]
    },
    {
      "responses": [{
        "is": {
          "statusCode": 200,
          "headers": { "Content-Type": "application/json" },
          "body": {
            "products": [
              {
                "id": "2599b7f4",
                "name": "The Midas Dogbowl",
                "description": "Pure gold"
              },
              {
                "id": "e1977c9e",
                "name": "Fishtank Amore",
                "description": "Show your fish some love"
              }
            ],
            "_links": { "next": "/products?page=2&itemsPerPage=2" }
          }
        }
      }]
    }
  ]
}'
```

作成したImposterは、`"predicates": [{ "equals": { "query": { "page": "2" } } `}]となっているようにGetパラメータにpage=2をつけてHTTPリクエストすると空のproducts情報をレスポンスとして返します。

```bash
$ curl -i http://localhost:3000/products?page=2
```

```Output
HTTP/1.1 200 OK
Content-Type: application/json
Connection: close
Date: Thu, 08 Mar 2018 14:18:44 GMT
Transfer-Encoding: chunked

{
    "products": []
}
```

それ以外のパラメータのリクエストに対しては、2つの製品情報が入ったレスポンスを返してきます。

```bash
$ curl -i http://localhost:3000/products?page=3
```

```Output
HTTP/1.1 200 OK
Content-Type: application/json
Connection: close
Date: Thu, 08 Mar 2018 14:19:34 GMT
Transfer-Encoding: chunked

{
    "products": [
        {
            "id": "2599b7f4",
            "name": "The Midas Dogbowl",
            "description": "Pure gold"
        },
        {
            "id": "e1977c9e",
            "name": "Fishtank Amore",
            "description": "Show your fish some love"
        }
    ],
    "_links": {
        "next": "/products?page=2&itemsPerPage=2"
    }
}
```

今回は基本的な使い方まででしたので、今後もっと試していきたいと思います。

## 参考・引用文献

- [mountebank](http://www.mbtest.org/)
- [Testing Microservices with Mountebank](https://www.manning.com/books/testing-microservices-with-mountebank)