欢迎光临
我们一直在努力

如何使用Mountebank和Node.js模拟服务

作者选择了开放互联网/言论自由基金作为Write for DOnations计划的一部分进行捐赠。

介绍

在复杂的面向服务的体系结构(SOA)中 ,程序通常需要调用多个服务来运行给定的工作流。 一旦一切就绪,这很好,但是如果你正在处理的代码需要一个仍处于开发阶段的服务,你可能会等待其他团队在开始你的工作之前完成他们的工作。 此外,出于测试目的,您可能需要与外部供应商服务进行交互,例如天气API或记录保存系统。 供应商通常不会为您提供所需数量的环境,并且通常无法轻松控制其系统上的测试数据。 在这些情况下,您控制之外的未完成的服务和服务可能会使代码测试变得令人沮丧。

所有这些问题的解决方案是创建服务模拟 服务模拟是模拟您将在最终产品中使用的服务的代码,但是比您在生产中使用的实际服务更轻,更简单,更容易控制。 您可以设置模拟服务以返回默认响应或特定测试数据,然后运行您感兴趣的软件进行测试,就好像依赖服务确实在那里一样。 因此,通过灵活的方式模拟服务可以使您的工作流程更快,更高效。

在企业环境中,制作模拟服务有时也称为服务虚拟化 服务虚拟化通常与昂贵的企业工具相关联,但您不需要昂贵的工具来模拟服务。 Mountebank是一个免费的开源服务模拟工具,可用于模拟HTTP服务,包括RESTSOAP服务。 您还可以使用它来模拟SMTPTCP请求。

在本指南中,您将使用Node.js和Mountebank构建两个灵活的服务模拟应用程序。 两个模拟服务都将监听HTTP中的REST请求的特定端口。 除了这种简单的模拟行为外,该服务还将从逗号分隔值 (CSV)文件中检索模拟数据。 在本教程之后,您将能够模拟各种服务行为,以便您可以更轻松地开发和测试应用程序。

先决条件

要学习本教程,您需要具备以下条件:

第1步 – 启动Node.js应用程序

在此步骤中,您将创建一个基本的Node.js应用程序,该应用程序将作为您的Mountebank实例的基础以及您将在后续步骤中创建的模拟服务。

注意:通过使用命令npm install -g mountebank全局安装,可以将Mountebank用作独立应用程序。 然后,您可以使用mb命令运行它,并使用REST请求添加模拟。

虽然这是让Mountebank启动并运行的最快方法,但是自己构建Mountebank应用程序允许您在应用程序启动时运行一组预定义的模拟,然后您可以将其存储在源代码管理中并与您的团队共享。 本教程将手动构建Mountebank应用程序以利用此功能。

首先,创建一个新目录以放入您的应用程序。您可以将其命名为任意名称,但在本教程中我们将其命名为app

mkdir app

使用以下命令进入新创建的目录:

cd app

要启动新的Node.js应用程序,请运行npm init并填写提示:

npm init

这些提示中的数据将用于填写package.json文件,该文件描述了您的应用程序是什么,它依赖的程序包以及它使用的不同脚本。 在Node.js应用程序中,脚本定义了构建,运行和测试应用程序的命令。 您可以使用提示的默认值或填写您的包名,版本号等。

完成此命令后,您将拥有一个基本的Node.js应用程序,包括package.json文件。

现在使用以下命令安装Mountebank npm软件包:

npm install -save mountebank

此命令获取Mountebank包并将其安装到您的应用程序中。 确保使用-save标志以使用Mountebank作为依赖项更新package.json文件。

接下来,将一个启动脚本添加到运行命令node src/index.js package.json中。 此脚本将应用程序的入口点定义为index.js ,您将在稍后的步骤中创建该入口点。

在文本编辑器中打开package.json 您可以使用您想要的任何文本编辑器,但本教程将使用nano。

nano package.json

导航到"scripts"部分并添加"start": "node src/index.js" 这将添加一个start命令来运行您的应用程序。

您的package.json文件应该与此类似,具体取决于您填写初始提示的方式:

应用程序/的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实例和两个模拟服务将监听的端口。

每次运行Mountebank或模拟服务的实例时,您都需要指定该服务将运行的网络端口(例如, http://localhost:5000/ )。 通过将这些设置放在设置文件中,应用程序的其他部分将能够在需要知道服务和Mountebank实例的端口号时导入这些设置。 虽然您可以直接将这些代码作为常量编写到应用程序中,但如果将它们存储在文件中,以后更改设置会更容易。 这样,您只需在一个地方更改值。

首先从app目录创建一个名为src的目录:

mkdir src

导航到刚刚创建的文件夹:

cd src

创建一个名为settings.js的文件,并在文本编辑器中打开它:

nano settings.js

接下来,添加主Mountebank实例的端口设置以及稍后将创建的两个模拟服务:

应用程序/ SRC / settings.js
module.exports = {    port: 5000,    hello_service_port: 5001,    customer_service_port: 5002}

此设置文件有三个条目: port: 5000将端口port: 5000分配给主Mountebank实例, hello_service_port: 5001将端口5001分配给您将在稍后步骤中创建的Hello World测试服务, customer_service_port: 5002将端口5002分配给模拟将使用CSV数据回复的服务应用。 如果此处的端口已被占用,请随意将其更改为您想要的任何内容。 module.exports =使您的其他文件可以导入这些设置。

在此步骤中,您使用了settings.js来定义Mountebank和您的模拟服务将监听的端口,并使这些设置可用于您应用的其他部分。 在下一步中,您将使用这些设置构建初始化脚本以启动Mountebank。

第3步 – 构建初始化脚本

在此步骤中,您将创建一个启动Mountebank实例的文件。 此文件将是应用程序的入口点,这意味着,当您运行应用程序时,此脚本将首先运行。 在构建新的服务模拟时,您将向此文件添加更多行。

src目录中,创建一个名为index.js的文件,并在文本编辑器中打开它:

nano index.js

要启动将在您在上一步中创建的settings.js文件中指定的端口上运行的Mountebank实例,请将以下代码添加到该文件中:

应用程序/ SRC / index.js
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: ['*']    });

这段代码做了三件事。 首先,它导入您之前安装的Mountebank npm软件包( const mb = require('mountebank'); )。 然后,它导入您在上一步中创建的设置模块( const settings = require('./settings'); )。 最后,它使用mb.create()创建Mountebank服务器的实例。

服务器将监听设置文件中指定的端口。 pidfilelogfileprotofile参数用于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响应:

{    "_links": {        "imposters": {            "href": "http://localhost:5000/imposters"        },        "config": {            "href": "http://localhost:5000/config"        },        "logs": {            "href": "http://localhost:5000/logs"        }    }}

Mountebank返回的JSON描述了可用于在Mountebank中添加或删除对象的三个不同端点。 通过使用curl向这些端点发送请求,您可以与Mountebank实例进行交互。

完成后,切换回第一个终端窗口并使用CTRL + C退出应用程序。 这将退出您的Node.js应用程序,以便您可以继续添加它。

现在您有一个成功运行Mountebank实例的应用程序。 在下一步中,您将创建一个Mountebank客户端,该客户端使用REST请求将模拟服务添加到您的Mountebank应用程序。

第4步 – 建立Mountebank客户端

Mountebank使用REST API进行通信。 您可以通过将HTTP请求发送到上一步中提到的不同端点来管理Mountebank实例的资源。 要添加模拟服务,请向imposters端点发送HTTP POST请求。 冒名顶替是Mountebank的模拟服务的名称。 根据您在模拟中所需的行为,Imposters可以是简单的也可以是复杂的。

在此步骤中,您将构建一个Mountebank客户端,以自动将POST请求发送到Mountebank服务。 您可以使用curl或Postman向imposters端点发送POST请求,但每次重新启动测试服务器时都必须发送相同的请求。 如果您正在运行具有多个模拟的示例API,那么编写客户端脚本来为您执行此操作会更有效。

首先安装node-fetch库:

npm install -save node-fetch

node-fetch为您提供了JavaScript Fetch API的实现,您可以使用它来编写较短的HTTP请求。 您可以使用标准的http库,但使用node-fetch是一种更轻量级的解决方案。

现在,创建一个客户端模块以向Mountebank发送请求。 您只需要发布冒名顶替者,因此该模块将有一个方法。

使用nano创建一个名为mountebank-helper.js的文件:

nano mountebank-helper.js

要设置客户端,请将以下代码放在文件中:

应用程序/ SRC /走江湖-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库和您的设置文件开始。 然后,该模块公开了一个名为postImposter的函数,该函数将服务postImposter发布到Mountebank。 接下来, body:确定该函数采用JSON.stringify(body) ,一个JavaScript对象。 这个对象就是你要发布到Mountebank服务的内容。 由于此方法在本地运行,因此您针对127.0.0.1localhost )运行请求。 fetch方法获取参数中发送的对象,并将POST请求发送到url

在此步骤中,您创建了一个Mountebank客户端,以便向Mountebank服务器发布新的模拟服务。 在下一步中,您将使用此客户端创建您的第一个模拟服务。

第5步 – 创建您的第一个模拟服务

在前面的步骤中,您构建了一个应用程序,用于创建Mountebank服务器和代码以调用该服务器。 现在是时候使用该代码来构建冒名顶替者或模拟服务。

在Mountebank,每个冒名顶替者包含存根 存根是配置集,用于确定冒名顶替者将给出的响应。 存根可以进一步划分为谓词响应的组合。 谓词是触发冒名顶替者响应的规则。 谓词可以使用许多不同类型的信息,包括URL,请求内容(使用XML或JSON)和HTTP方法。

模型 – 视图 – 控制器(MVC)应用程序的角度来看,冒名顶替者像控制器一样,而存根像行动控制器中的动作。 谓词是指向特定控制器操作的路由规则。

要创建第一个模拟服务,请创建一个名为hello-service.js的文件。 该文件将包含模拟服务的定义。

在文本编辑器中打开hello-service.js

nano hello-service.js

然后添加以下代码:

应用程序/ src目录/ 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服务器。 此代码将添加一个新的模拟服务,该服务监听对根url GET请求,并在获取一个时返回{ 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)                    }                }            ]        }    ];...

这个存根有两个部分。 谓词部分正在寻找对根( / )URL的GET请求。 这意味着当有人向模拟服务的根URL发送GET请求时, stubs将返回响应。 存根的第二部分是responses数组。 在这种情况下,有一个响应,它返回HTTP状态代码为200的JSON结果。

最后一步定义了包含该存根的冒名顶替者:

...    const imposter = {        port: settings.hello_service_port,        protocol: 'http',        stubs: stubs    };...

这是您要发送到/imposters端点的对象,以创建使用单个端点模拟服务的冒名顶替者。 上面的代码通过将port设置为您在设置文件中确定的端口,将protocol设置为HTTP以及将stubs分配为冒名顶替的存根来定义冒名顶替者。

现在你有了一个模拟服务,代码将它发送到Mountebank服务器:

...    return mbHelper.postImposter(imposter);...

如前所述,Mountebank使用REST API来管理其对象。 上面的代码使用您之前定义的postImposter()函数向服务器发送POST请求以激活服务。

完成hello-service.js ,保存并退出该文件。

接下来,在index.js调用新创建的addService()函数。 在文本编辑器中打开文件:

nano index.js

要确保在创建Mountebank实例时调用该函数,请添加以下突出显示的行:

应用程序/ SRC / index.js
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 promise是一个直到稍后才确定其值的对象。 这可以用于简化异步函数调用。 在前面的代码中, .then(function(){...})函数在Mountebank服务器初始化时执行,这在promise解析时发生。

保存并退出index.js

要测试在Mountebank初始化时创建模拟服务,请启动应用程序:

npm start

Node.js进程将占用终端,因此打开一个新的终端窗口并向http://localhost:5001/发送GET请求:

curl http://localhost:5001

您将收到以下响应,表示该服务正在运行:

{"message": "hello world"}

现在您已经测试了应用程序,切换回第一个终端窗口并使用CTRL + C退出Node.js应用程序。

在此步骤中,您创建了第一个模拟服务。 这是一个测试服务模拟,它返回hello world以响应GET请求。 这个模拟用于演示目的; 通过构建一个小型Express应用程序,它并没有真正给你带来任何好处。 在下一步中,您将创建一个更复杂的模拟,利用Mountebank的一些功能。

第6步 – 构建数据支持的模拟服务

虽然您在上一步中创建的服务类型适用于某些情况,但大多数测试需要更复杂的响应集。 在此步骤中,您将创建一个服务,该服务从URL获取参数并使用它在CSV文件中查找记录。

首先,回到主app目录:

cd ~/app

创建一个名为data的文件夹:

mkdir data

打开名为customers.csv的客户数据文件:

nano data/customers.csv

添加以下测试数据,以便您的模拟服务可以检索:

应用程序/数据/ customers.csv
id,first_name,last_name,email,favorite_color 1,Erda,Birkin,[email protected],Aquamarine2,Cherey,Endacott,[email protected],Fuscia3,Shalom,Westoff,[email protected],Red4,Jo,Goulborne,[email protected],Red

这是由API 模拟工具Mockaroo生成的虚假客户数据,类似于您将加载到服务本身的customers表中的虚假数据。

保存并退出该文件。

然后,在src目录中创建一个名为customer-service.js的新模块:

nano src/customer-service.js

要创建监听/customers/ endpoint上的GET请求的冒名顶替者,请添加以下代码:

应用程序/ src目录/客户service.js
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 };

此代码定义了一个服务模拟,用于查找URL格式为customers/<id> GET请求。 收到请求后,它将在URL中查询客户的id ,然后从CSV文件中返回相应的记录。

此代码使用了比您在上一步中创建的hello服务更多的Mountebank功能。 首先,它使用了Mountebank的一项称为行为的功能。 行为是一种向存根添加功能的方法。 在这种情况下,您使用lookup行为在CSV文件中lookup记录:

...  _behaviors: {      lookup: [          {              "key": {                "from": "path",                "using": { "method": "regex", "selector": "/customers/(.*)$" },                "index": 1              },              "fromDataSource": {                "csv": {                  "path": "data/customers.csv",                  "keyColumn": "id"                }              },              "into": "${row}"            }      ]  }...

key属性使用正则表达式来解析传入路径。 在这种情况下,您将获取customers/ URL中的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

添加突出显示的行:

应用程序/ 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。 这将隐藏提示,因此打开另一个终端窗口。 通过向localhost:5002/customers/3发送GET请求来测试您的服务。 这将在id 3下查找客户信息。

curl localhost:5002/customers/3

您将看到以下响应:

{    "firstName": "Shalom",    "lastName": "Westoff",    "favColor": "Red"}

在此步骤中,您创建了一个模拟服务,该服务从CSV文件读取数据并将其作为JSON响应返回。 从这里开始,您可以继续构建更复杂的模拟,以匹配您需要测试的服务。

结论

在本文中,您使用Mountebank和Node.js创建了自己的服务模拟应用程序。 现在,您可以构建模拟服务并与团队共享。 无论是涉及供应商服务的复杂场景,还是需要在等待其他团队完成工作时进行测试,您都可以通过创建模拟服务来保持团队的移动。

如果您想了解更多关于Mountebank的信息,请查看他们的文档 如果您想将这个应用程序容器化,请查看使用Docker Compose对Containrizing Node.js应用程序进行开发 如果您想在类似生产环境中运行此应用程序,请查看如何在Ubuntu 18.04上设置Node.js生产应用程序

赞(0) 打赏
未经允许不得转载:老赵部落 » 如何使用Mountebank和Node.js模拟服务

评论 抢沙发