文本

使用Nock进行集成测试中的模拟依赖项

软件行业正朝着集成测试的方向发展,以测试其提供代码信心的能力。然而,它们的设置是冗长乏味的,而且其目的的一个基本部分的外部影响会降低性能和信任度。在使用nock进行集成测试时模拟第三方依赖关系有助于降低复杂性,提高一致性和可靠性。 软件行业因其提供代码置信度的能力而趋向于集成测试。 但是,他们的工作乏味,而且外部影响(其目标的基本组成部分)会降低绩效和信任度。 使用nock模拟集成测试中的第三方依赖项有助于降低复杂性并提高一致性和可靠性。 与Nock的集成测试 Nock是一个JavaScript库,可模拟外部API请求。 该库对于集成测试非常强大,因为它与内部实现无关。 API发出的请求不是内部详细信息的一部分,而是合同。 例 下图是TODO API的人为示例,该API创建一个事件以安排提醒。

客户端调用todo API,该API插入todo数据库。 副作用是,事件API将提醒插入事件数据库中。 开发人员无需查看代码即可为该体系结构编写测试。 通过使用测试驱动的开发,他们可以测试行为,而不是不重要的实现细节。 这与打算使用库的方式紧密一致

it("creates a todo", async function () {
  const response = await request(app)
    .post("/v1/todo")
    .set("Content-Type", "application/json")
    .set("Accept", "application/json")
    .send({
      title: "Make resolutions",
      reminder: null,
    });

  expect(response.status).to.equal(201);
  expect(response.body.id).to.be.a.uuid("v4");
  // Additional assertions
});	

此测试未使用nock,以证明它可能不适用于任何地方。 尽管测试依赖于活动数据库,但在本地或作为CI管道的一部分启动Docker容器很简单。 尽管我们可以模拟数据库交互,但弊大于利。 流行数据库的开发人员将依赖项打包在维护良好且稳定的Docker容器中。 模拟他们的所有交互将是乏味的,并且可能掩盖致命的错误。 下一个测试涉及与外部API的交互:

it("creates a reminder", async function () {
  let eventApiCalled = false;

  nock("https://events-api:3000")
    .post("/v1/reminder")
    .reply(201, (uri, requestBody) => {
      eventApiCalled = true;
      return { id: "some-uuid", other: "attribute" };
    });

  const response = await request(app)
    .post("/v1/todo")
    .set("Content-Type", "application/json")
    .set("Accept", "application/json")
    .send({
      title: "Make resolutions",
      reminder: {
        date: "2020-01-01T00:00:00Z",
      },
    });

  expect(response.status).to.equal(201);
  expect(eventApiCalled).to.be.true();
});

在这种情况下,todo API和事件API之间的协定是已知的。 模拟是有意义的,因为事件API可以针对相同的合同拥有自己的一组测试。 通过使用nock模拟,可以将事件API与todo API分离。 有些人可能不认为这是一个真正的集成测试,但是包含一个真正的连接会带来一些缺点。 真正的HTTP连接的缺点 开发人员需要在本地运行外部API,并将其作为CI管道的一部分 这会很快失去控制。 事件API可能依赖于其他五个服务,而这些服务可能依赖于其他十个服务。 如果这些服务不是可选的,则安装程序中还需要包括每个服务。 依赖关系将todo API耦合到事件API架构 为了正确运行事件API,根据示例图,必须包括本地数据库。 其他必需的组件,例如Redis缓存或外部日志记录系统,也可以存在。 如果事件API的维护者决定将数据库从MySQL交换到Postgres,则todo API的CI管道将中断。 测试会产生假阴性结果,并且不能反映现实情况:todo API可以在交换的数据库中正常运行。 延迟增加了测试时间 通过调出真实的API(尤其是速度较慢的API),每次测试花费的时间会稍长一些。 尽管在一些测试中这似乎是微不足道的,但延迟可能会在产品的使用寿命内累积。 模拟连接的缺点 待办事项API必须信任事件API才能不违反合同 Pact JS之类的框架可以帮助内部API解决此问题,但不能解决第三方问题。 在本文中,我故意不关注Pact,因为许多开发人员很难获得支持和支持。 💡有关Pact的更多信息,我建议您浏览这份详尽的《消费者合同测试指南》。 其他提示和技巧 测试消耗了堆栈中的所有Nocks 如果测试两次调用API,则nock仅对第一次调用有效。 要绕过此行为,可以使用持久性声明它们:

nock("https://event-api:3000")
  .post("/v1/events")
  .reply(201, function () {
    // Create and return event
  })
  .persist()	

但是,默认行为对于公开意外调用很有用。如果排除了persist选项,那么创建两个事件将使测试失败。

nock(/.*amazon.com.*/)
  .put(/s3/)
  .reply(200, {...}

这可以帮助克服带有UUID的网址。 Nock完全匹配网址而不使用正则表达式,并且任何不匹配的调用都将无法处理并引发错误。 查询参数旁路

nock("https://events-api:3000")
  .get("/v1/events")
  .query(true)

忽略查询参数可以提供一种方法来返回相同的数据,而不管参数是什么。应答错误

nock("https://events-api:3000")
  .post("/v1/reminder")
  .reply(500, "...")

这是模拟的另一个优点。 考虑todo API示例:如果创建提醒的调用失败,则它应回滚创建todo的事务并返回500错误。 在真实世界的连接中,除了关闭服务之外,可能很难找到一种自然的方法来使事件API失败。 该库提供了更多实用程序,在文档中提到了这些实用程序:https://github.com/nock/nock。 结论 Nock更改了集成测试,以减少对实际连接性的关注,而将更多精力放在遵守合同上。 通过模拟API层,我们可以控制与第三方服务的交互,即使它们在公司内部也是如此。 使用nock编写集成测试可以显着提高速度和测试可靠性,但必须以信任合同为代价。

author

石头 磊哥 seven 随便叫

company

thoughtworks(离职了。。。。)

大家好,本人不才,目前依旧混迹于thoughtworks,做着一名看起来像全栈的QA,兴趣爱好前端,目前是thoughtworks 西安QA社区的leader,如果有兴趣分享话题,或者想加入tw,可以找我

roles

QA(营生) dev(front-end dev 兴趣爱好)

联系方式

如果想转载或者高薪挖我 请直接联系我 哈哈

wechat:

qileiwangnan

email:

qileilove@gmail.com