MountebankとNode.jsを使用してサービスをモックする方法
著者は、 Open Internet / Free Speech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
複雑なサービス指向アーキテクチャー(SOA)では、プログラムが特定のワークフローを実行するために複数のサービスを呼び出す必要があることがよくあります。 すべてが整ったらこれで問題ありませんが、作業中のコードにまだ開発中のサービスが必要な場合は、他のチームが作業を終了するのを待ってから作業を開始することができます。 さらに、テストの目的で、天気APIや記録管理システムなどの外部ベンダーサービスとのやり取りが必要になる場合があります。 ベンダーは通常、必要な数の環境を提供せず、システム上のテストデータを簡単に制御できないことがよくあります。 このような状況では、未完成のサービスや制御できないサービスによって、コードテストがイライラする可能性があります。
これらすべての問題の解決策は、サービスモックを作成することです。 サービスモックは、最終製品で使用するサービスをシミュレートするコードですが、本番環境で使用する実際のサービスよりも軽量で、複雑さが少なく、制御が簡単です。 デフォルトの応答または特定のテストデータを返すようにモックサービスを設定してから、依存するサービスが実際に存在するかのように、テストするソフトウェアを実行できます。 このため、サービスをモックする柔軟な方法を使用すると、ワークフローをより高速かつ効率的にすることができます。
企業の設定では、模擬サービスの作成はサービス仮想化と呼ばれることもあります。 多くの場合、サービスの仮想化は高価なエンタープライズツールに関連付けられていますが、サービスをモックするために高価なツールは必要ありません。 Mountebank は、RESTやSOAPサービスなどのHTTPサービスをモックするために使用できる無料のオープンソースサービスモックツールです。 また、SMTPまたはTCPリクエストをモックするために使用することもできます。
このガイドでは、 Node.js とMountebankを使用して、2つの柔軟なサービスモックアプリケーションを構築します。 両方のモックサービスは、HTTPでのRESTリクエストの特定のポートをリッスンします。 この単純なモック動作に加えて、サービスはカンマ区切り値(CSV)ファイルからモックデータも取得します。 このチュートリアルの後、アプリケーションの開発とテストをより簡単に行えるように、あらゆる種類のサービス動作をモックできるようになります。
前提条件
このチュートリアルに従うには、次のものが必要です。
-
マシンにインストールされているNode.jsのバージョン8.10.0以降。 このチュートリアルでは、バージョン8.10.0を使用します。 Node.jsをインストールするには、 Ubuntu18.04にNode.jsをインストールする方法またはmacOSにNode.jsをインストールしてローカル開発環境を作成する方法を確認してください。
-
cURLやPostmanなどのHTTPリクエストを作成するためのツール。 ほとんどのマシンにデフォルトでインストールされているため、このチュートリアルではcURLを使用します。 お使いのマシンにcURLがない場合は、インストールドキュメントを参照してください。
ステップ1—Node.jsアプリケーションを起動する
このステップでは、Mountebankインスタンスのベースとして機能する基本的なNode.jsアプリケーションと、後のステップで作成するモックサービスを作成します。
注: Mountebankは、コマンドnpm install -g mountebank
を使用してグローバルにインストールすることにより、スタンドアロンアプリケーションとして使用できます。 次に、mb
コマンドで実行し、RESTリクエストを使用してモックを追加できます。
これはMountebankを起動して実行するための最速の方法ですが、Mountebankアプリケーションを自分で構築すると、アプリの起動時に事前定義されたモックのセットを実行でき、ソース管理に保存してチームと共有できます。 このチュートリアルでは、これを利用するためにMountebankアプリケーションを手動でビルドします。
まず、アプリケーションを配置するための新しいディレクトリを作成します。 好きな名前を付けることができますが、このチュートリアルではapp
という名前を付けます。
- mkdir app
次のコマンドを使用して、新しく作成したディレクトリに移動します。
- cd app
新しいNode.jsアプリケーションを開始するには、npm init
を実行し、プロンプトに入力します。
- npm init
これらのプロンプトからのデータは、package.json
ファイルに入力するために使用されます。このファイルには、アプリケーションとは何か、アプリケーションが依存するパッケージ、および使用するさまざまなスクリプトが記述されています。 Node.jsアプリケーションでは、スクリプトは、アプリケーションをビルド、実行、およびテストするコマンドを定義します。 プロンプトのデフォルトを使用するか、パッケージ名、バージョン番号などを入力できます。
このコマンドを完了すると、package.json
ファイルを含む基本的なNode.jsアプリケーションが作成されます。
次に、以下を使用してMountebanknpmパッケージをインストールします。
- npm install -save mountebank
このコマンドは、Mountebankパッケージを取得し、アプリケーションにインストールします。 Mountebankを依存関係としてpackage.json
ファイルを更新するには、必ず-save
フラグを使用してください。
次に、コマンドnode src/index.js
を実行する開始スクリプトをpackage.json
に追加します。 このスクリプトは、アプリのエントリポイントをindex.js
として定義します。これは、後の手順で作成します。
テキストエディタでpackage.json
を開きます。 任意のテキストエディタを使用できますが、このチュートリアルではnanoを使用します。
- nano package.json
"scripts"
セクションに移動し、"start": "node src/index.js"
という行を追加します。 これにより、アプリケーションを実行するためのstart
コマンドが追加されます。
package.json
ファイルは、最初のプロンプトの入力方法に応じて、次のようになります。
{
"name": "diy-service-virtualization",
"version": "1.0.0",
"description": "An application to mock services.",
"main": "index.js",
"scripts": {
"start": "node src/index.js"
},
"author": "Dustin Ewers",
"license": "MIT",
"dependencies": {
"mountebank": "^2.0.0"
}
}
これで、アプリを作成し、Mountebankをインストールし、開始スクリプトを追加して構築したMountebankアプリケーションのベースができました。 次に、アプリケーション固有の設定を保存するための設定ファイルを追加します。
ステップ2—設定ファイルを作成する
このステップでは、Mountebankインスタンスと2つのモックサービスがリッスンするポートを決定する設定ファイルを作成します。
Mountebankまたはモックサービスのインスタンスを実行するたびに、そのサービスを実行するネットワークポートを指定する必要があります(http://localhost:5000/
など)。 これらを設定ファイルに入れることで、アプリケーションの他の部分は、サービスとMountebankインスタンスのポート番号を知る必要があるときはいつでもこれらの設定をインポートできるようになります。 これらを定数としてアプリケーションに直接コーディングすることもできますが、ファイルに保存すると、後で設定を変更するのが簡単になります。 このように、1か所で値を変更するだけで済みます。
app
ディレクトリからsrc
というディレクトリを作成することから始めます。
- mkdir src
作成したフォルダに移動します。
- cd src
settings.js
というファイルを作成し、テキストエディタで開きます。
- nano settings.js
次に、メインのMountebankインスタンスと、後で作成する2つのモックサービスのポートの設定を追加します。
module.exports = {
port: 5000,
hello_service_port: 5001,
customer_service_port: 5002
}
この設定ファイルには3つのエントリがあります。port: 5000
はポート5000
をメインのMountebankインスタンスに割り当て、hello_service_port: 5001
はポート5001
をHelloWorldテストサービスに割り当てます。後の手順で作成し、customer_service_port: 5002
はCSVデータで応答するモックサービスアプリにポート5002
を割り当てます。 ここのポートが占有されている場合は、自由に変更してください。 module.exports =
を使用すると、他のファイルでこれらの設定をインポートできます。
このステップでは、settings.js
を使用して、Mountebankとモックサービスがリッスンするポートを定義し、これらの設定をアプリの他の部分で利用できるようにしました。 次のステップでは、これらの設定を使用して初期化スクリプトを作成し、Mountebankを起動します。
ステップ3—初期化スクリプトの作成
このステップでは、Mountebankのインスタンスを開始するファイルを作成します。 このファイルはアプリケーションのエントリポイントになります。つまり、アプリを実行すると、このスクリプトが最初に実行されます。 新しいサービスモックを作成するときに、このファイルにさらに行を追加します。
src
ディレクトリから、index.js
というファイルを作成し、テキストエディタで開きます。
- nano index.js
前の手順で作成したsettings.js
ファイルで指定されたポートで実行されるMountebankのインスタンスを開始するには、ファイルに次のコードを追加します。
const mb = require('mountebank');
const settings = require('./settings');
const mbServerInstance = mb.create({
port: settings.port,
pidfile: '../mb.pid',
logfile: '../mb.log',
protofile: '../protofile.json',
ipWhitelist: ['*']
});
このコードは3つのことを行います。 まず、以前にインストールしたMountebank npmパッケージ(const mb = require('mountebank');
)をインポートします。 次に、前の手順で作成した設定モジュール(const settings = require('./settings');
)をインポートします。 最後に、mb.create()
を使用してMountebankサーバーのインスタンスを作成します。
サーバーは、設定ファイルで指定されたポートでリッスンします。 pidfile
、logfile
、およびprotofile
パラメーターは、Mountebankが内部でプロセスIDを記録し、ログを保持する場所を指定し、カスタムをロードするようにファイルを設定するために使用するファイル用です。プロトコルの実装。 ipWhitelist
設定は、Mountebankサーバーとの通信を許可するIPアドレスを指定します。 この場合、任意のIPアドレスに公開しています。
ファイルを保存して終了します。
このファイルを配置したら、次のコマンドを入力してアプリケーションを実行します。
- npm start
コマンドプロンプトが消え、次のように表示されます。
- info: [mb:5000] mountebank v2.0.0 now taking orders - point your browser to http://localhost:5000/ for help
これは、アプリケーションが開いていて、リクエストを受け取る準備ができていることを意味します。
次に、進捗状況を確認します。 新しいターミナルウィンドウを開き、curl
を使用して、次のGET
要求をMountebankサーバーに送信します。
- curl http://localhost:5000/
これにより、次のJSON応答が返されます。
Output{
"_links": {
"imposters": {
"href": "http://localhost:5000/imposters"
},
"config": {
"href": "http://localhost:5000/config"
},
"logs": {
"href": "http://localhost:5000/logs"
}
}
}
Mountebankが返すJSONは、Mountebankでオブジェクトを追加または削除するために使用できる3つの異なるエンドポイントを記述しています。 curl
を使用してこれらのエンドポイントにリクエストを送信することにより、Mountebankインスタンスと対話できます。
完了したら、最初のターミナルウィンドウに戻り、CTRL
+C
を使用してアプリケーションを終了します。 これでNode.jsアプリが終了するため、引き続き追加できます。
これで、Mountebankのインスタンスを正常に実行するアプリケーションができました。 次のステップでは、RESTリクエストを使用して模擬サービスをMountebankアプリケーションに追加するMountebankクライアントを作成します。
ステップ4—Mountebankクライアントの構築
Mountebankは、RESTAPIを使用して通信します。 前の手順で説明したさまざまなエンドポイントにHTTPリクエストを送信することで、Mountebankインスタンスのリソースを管理できます。 モックサービスを追加するには、HTTPPOST
リクエストを詐欺師のエンドポイントに送信します。 imposter は、Mountebankの模擬サービスの名前です。 詐欺師は、モックで必要な動作に応じて、単純なものから複雑なものまであります。
このステップでは、Mountebankクライアントを構築して、POST
リクエストをMountebankサービスに自動的に送信します。 curl
またはPostmanを使用して、POST
リクエストを詐欺師のエンドポイントに送信できますが、テストサーバーを再起動するたびに同じリクエストを送信する必要があります。 複数のモックを使用してサンプルAPIを実行している場合は、これを行うためのクライアントスクリプトを作成する方が効率的です。
node-fetch
ライブラリをインストールすることから始めます。
- npm install -save node-fetch
node-fetch library は、JavaScript Fetch APIの実装を提供します。これを使用して、より短いHTTPリクエストを作成できます。 標準のhttp
ライブラリを使用できますが、node-fetch
を使用する方が軽量なソリューションです。
次に、Mountebankにリクエストを送信するクライアントモジュールを作成します。 詐欺師を投稿するだけでよいので、このモジュールには1つの方法があります。
nano
を使用して、mountebank-helper.js
というファイルを作成します。
- nano mountebank-helper.js
クライアントを設定するには、ファイルに次のコードを入力します。
const fetch = require('node-fetch');
const settings = require('./settings');
function postImposter(body) {
const url = `http://127.0.0.1:${settings.port}/imposters`;
return fetch(url, {
method:'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
}
module.exports = { postImposter };
このコードは、node-fetch
ライブラリと設定ファイルをプルすることから始まります。 次に、このモジュールは、サービスモックをMountebankに投稿するpostImposter
と呼ばれる関数を公開します。 次に、body:
は、関数がJavaScriptオブジェクトであるJSON.stringify(body)
を取ることを決定します。 このオブジェクトは、MountebankサービスにPOST
するものです。 このメソッドはローカルで実行されているため、127.0.0.1
(localhost
)に対してリクエストを実行します。 fetchメソッドは、パラメーターで送信されたオブジェクトを受け取り、POST
要求をurl
に送信します。
このステップでは、新しいモックサービスをMountebankサーバーに投稿するMountebankクライアントを作成しました。 次のステップでは、このクライアントを使用して最初の模擬サービスを作成します。
ステップ5—最初の模擬サービスを作成する
前の手順では、Mountebankサーバーとそのサーバーを呼び出すコードを作成するアプリケーションを作成しました。 次に、そのコードを使用して、詐欺師または模擬サービスを構築します。
Mountebankでは、各詐欺師にはスタブが含まれています。 スタブは、詐欺師が与える応答を決定する構成セットです。 スタブは、述語と応答の組み合わせにさらに分割できます。 述語は、詐欺師の応答をトリガーするルールです。 述語は、URL、リクエストコンテンツ(XMLまたはJSONを使用)、HTTPメソッドなど、さまざまな種類の情報を使用できます。
Model-View-Controller(MVC)アプリの観点から見ると、詐欺師はコントローラーのように機能し、スタブはそのコントローラー内のアクションのように機能します。 述語は、特定のコントローラーアクションを指すルーティングルールです。
最初のモックサービスを作成するには、hello-service.js
というファイルを作成します。 このファイルには、モックサービスの定義が含まれています。
テキストエディタでhello-service.js
を開きます。
- nano hello-service.js
次に、次のコードを追加します。
const mbHelper = require('./mountebank-helper');
const settings = require('./settings');
function addService() {
const response = { message: "hello world" }
const stubs = [
{
predicates: [ {
equals: {
method: "GET",
"path": "/"
}
}],
responses: [
{
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(response)
}
}
]
}
];
const imposter = {
port: settings.hello_service_port,
protocol: 'http',
stubs: stubs
};
return mbHelper.postImposter(imposter);
}
module.exports = { addService };
このコードは、述語と応答を含む単一のスタブを持つ詐欺師を定義します。 次に、そのオブジェクトをMountebankサーバーに送信します。 このコードは、GET
リクエストをルートurl
にリッスンし、取得すると{ message: "hello world" }
を返す新しいモックサービスを追加します。
上記のコードが作成するaddService()
関数を見てみましょう。 まず、応答メッセージhello world
を定義します。
const response = { message: "hello world" }
...
次に、スタブを定義します。
...
const stubs = [
{
predicates: [ {
equals: {
method: "GET",
"path": "/"
}
}],
responses: [
{
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(response)
}
}
]
}
];
...
このスタブには2つの部分があります。 述語部分は、ルート(/
)URLへのGET
要求を探しています。 これは、誰かがGET
リクエストをモックサービスのルートURLに送信すると、stubs
が応答を返すことを意味します。 スタブの2番目の部分は、responses
アレイです。 この場合、200
のHTTPステータスコードを持つJSON結果を返す1つの応答があります。
最後のステップでは、そのスタブを含む詐欺師を定義します。
...
const imposter = {
port: settings.hello_service_port,
protocol: 'http',
stubs: stubs
};
...
これは、/imposters
エンドポイントに送信して、単一のエンドポイントでサービスをモックする詐欺師を作成するオブジェクトです。 上記のコードは、設定ファイルで決定したポートにport
を設定し、protocol
をHTTPに設定し、stubs
を詐欺師のスタブとして割り当てることにより、詐欺師を定義します。
これで模擬サービスができたので、コードはそれをMountebankサーバーに送信します。
...
return mbHelper.postImposter(imposter);
...
前述のように、MountebankはRESTAPIを使用してオブジェクトを管理します。 上記のコードは、前に定義したpostImposter()
関数を使用して、POST
要求をサーバーに送信し、サービスをアクティブ化します。
hello-service.js
を終了したら、ファイルを保存して終了します。
次に、index.js
で新しく作成したaddService()
関数を呼び出します。 テキストエディタでファイルを開きます。
- nano index.js
Mountebankインスタンスの作成時に関数が確実に呼び出されるようにするには、次の強調表示された行を追加します。
const mb = require('mountebank');
const settings = require('./settings');
const helloService = require('./hello-service');
const mbServerInstance = mb.create({
port: settings.port,
pidfile: '../mb.pid',
logfile: '../mb.log',
protofile: '../protofile.json',
ipWhitelist: ['*']
});
mbServerInstance.then(function() {
helloService.addService();
});
Mountebankインスタンスが作成されると、promiseが返されます。 約束は、後でまでその値を決定しないオブジェクトです。 これは、非同期関数呼び出しを単純化するために使用できます。 上記のコードでは、.then(function(){...})
関数は、Mountebankサーバーが初期化されたときに実行されます。これは、Promiseが解決されたときに発生します。
index.js
を保存して終了します。
Mountebankの初期化時にモックサービスが作成されることをテストするには、アプリケーションを起動します。
- npm start
Node.jsプロセスがターミナルを占有するため、新しいターミナルウィンドウを開き、GET
リクエストをhttp://localhost:5001/
に送信します。
- curl http://localhost:5001
サービスが機能していることを示す次の応答が表示されます。
Output{"message": "hello world"}
アプリケーションをテストしたので、最初のターミナルウィンドウに戻り、CTRL
+C
を使用してNode.jsアプリケーションを終了します。
このステップでは、最初の模擬サービスを作成しました。 これは、GET
要求に応答してhello world
を返すテストサービスモックです。 このモックはデモンストレーションを目的としています。 小さなExpressアプリケーションを構築することで得られなかったものは実際には何も得られません。 次のステップでは、Mountebankの機能のいくつかを利用するより複雑なモックを作成します。
ステップ6—データに基づく模擬サービスの構築
前の手順で作成したサービスの種類は一部のシナリオでは問題ありませんが、ほとんどのテストでは、より複雑な一連の応答が必要です。 このステップでは、URLからパラメーターを取得し、それを使用してCSVファイルのレコードを検索するサービスを作成します。
まず、メインのapp
ディレクトリに戻ります。
- cd ~/app
data
というフォルダーを作成します。
- mkdir data
customers.csv
という顧客データのファイルを開きます。
- nano data/customers.csv
次のテストデータを追加して、モックサービスが取得できるものを用意します。
id,first_name,last_name,email,favorite_color
1,Erda,Birkin,[email protected],Aquamarine
2,Cherey,Endacott,[email protected],Fuscia
3,Shalom,Westoff,[email protected],Red
4,Jo,Goulborne,[email protected],Red
これは、APIモックツール Mockaroo によって生成された偽の顧客データであり、サービス自体の顧客テーブルにロードする偽のデータに似ています。
ファイルを保存して終了します。
次に、src
ディレクトリにcustomer-service.js
という新しいモジュールを作成します。
- nano src/customer-service.js
/customers/
エンドポイントでGET
リクエストをリッスンする詐欺師を作成するには、次のコードを追加します。
const mbHelper = require('./mountebank-helper');
const settings = require('./settings');
function addService() {
const stubs = [
{
predicates: [{
and: [
{ equals: { method: "GET" } },
{ startsWith: { "path": "/customers/" } }
]
}],
responses: [
{
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: '{ "firstName": "${row}[first_name]", "lastName": "${row}[last_name]", "favColor": "${row}[favorite_color]" }'
},
_behaviors: {
lookup: [
{
"key": {
"from": "path",
"using": { "method": "regex", "selector": "/customers/(.*)$" },
"index": 1
},
"fromDataSource": {
"csv": {
"path": "data/customers.csv",
"keyColumn": "id"
}
},
"into": "${row}"
}
]
}
}
]
}
];
const imposter = {
port: settings.customer_service_port,
protocol: 'http',
stubs: stubs
};
return mbHelper.postImposter(imposter);
}
module.exports = { addService };
このコードは、customers/<id>
のURL形式でGET
リクエストを検索するサービスモックを定義します。 リクエストを受信すると、顧客のid
のURLを照会し、CSVファイルから対応するレコードを返します。
このコードは、前の手順で作成したhello
サービスよりもいくつかのMountebank機能を使用しています。 まず、の動作と呼ばれるMountebankの機能を使用します。 ビヘイビアーは、スタブに機能を追加する方法です。 この場合、lookup
の動作を使用して、CSVファイルのレコードを検索しています。
...
_behaviors: {
lookup: [
{
"key": {
"from": "path",
"using": { "method": "regex", "selector": "/customers/(.*)$" },
"index": 1
},
"fromDataSource": {
"csv": {
"path": "data/customers.csv",
"keyColumn": "id"
}
},
"into": "${row}"
}
]
}
...
key
プロパティは、正規表現を使用して着信パスを解析します。 この場合、URLのcustomers/
の後に続くid
を使用しています。
fromDataSource
プロパティは、テストデータの保存に使用しているファイルを指します。
into
プロパティは、結果を変数${row}
に挿入します。 その変数は、次のbody
セクションで参照されます。
...
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: '{ "firstName": "${row}[first_name]", "lastName": "${row}[last_name]", "favColor": "${row}[favorite_color]" }'
},
...
行変数は、応答の本文に入力するために使用されます。 この場合、それは顧客データを含むJSON文字列です。
ファイルを保存して終了します。
次に、index.js
を開いて、新しいサービスモックを初期化関数に追加します。
- nano src/index.js
強調表示された行を追加します。
const mb = require('mountebank');
const settings = require('./settings');
const helloService = require('./hello-service');
const customerService = require('./customer-service');
const mbServerInstance = mb.create({
port: settings.port,
pidfile: '../mb.pid',
logfile: '../mb.log',
protofile: '../protofile.json',
ipWhitelist: ['*']
});
mbServerInstance.then(function() {
helloService.addService();
customerService.addService();
});
ファイルを保存して終了します。
次に、npm start
でMountebankを起動します。 これによりプロンプトが非表示になるため、別のターミナルウィンドウを開きます。 GET
リクエストをlocalhost:5002/customers/3
に送信して、サービスをテストします。 これにより、id
3
の下の顧客情報が検索されます。
- curl localhost:5002/customers/3
次の応答が表示されます。
Output{
"firstName": "Shalom",
"lastName": "Westoff",
"favColor": "Red"
}
このステップでは、CSVファイルからデータを読み取り、それをJSON応答として返すモックサービスを作成しました。 ここから、テストする必要のあるサービスに一致する、より複雑なモックを構築し続けることができます。
結論
この記事では、MountebankとNode.jsを使用して独自のサービスモックアプリケーションを作成しました。 これで、模擬サービスを構築してチームと共有できます。 テストする必要のあるベンダーサービスを含む複雑なシナリオでも、別のチームが作業を終了するのを待つ間の単純なモックでも、モックサービスを作成することでチームを動かし続けることができます。
Mountebankについて詳しく知りたい場合は、Mountebankのドキュメントを確認してください。 このアプリケーションをコンテナ化する場合は、DockerComposeを使用した開発用のNode.jsアプリケーションのコンテナ化を確認してください。 このアプリケーションを本番環境のような環境で実行したい場合は、 Ubuntu18.04で本番用にNode.jsアプリケーションをセットアップする方法を確認してください。