【问题标题】:How to stub a service with Sinon in an integration test?如何在集成测试中使用 Sinon 存根服务?
【发布时间】:2020-09-18 09:34:09
【问题描述】:

我正在尝试在 express 中为我的 api 做一些集成测试。

我的 API 结构类似于:

app -> routes -> controllers -> services

因为我已经进行了单元测试,所以我的想法只是测试所有组件是否以正确的方式连接。

所以我的想法是用Sinon为服务创建一个存根,并且只用supertest检查控制器的响应。

当我运行单个测试时,一切正常。问题是当我为不同的控制器运行多个单元测试时,存根在第二次运行中不起作用。

我认为是因为应用程序已经作为模块保存在缓存中,所以 sinon 无法 stub 服务。

我的一些代码示例:

controller.js

const httpStatus = require('http-status');
const { service } = require('../services/croupier');

/**
 * Execute lambda tasks for candidates
 * @public
 */
exports.task = async (req, res, next) => {
  try {
    const result = await service({
      body: req.body,
      authorizer: req.authorizer
    });
    console.log('res', result);

    res.status(httpStatus.OK).json(result);
  } catch (error) {
    next(error);
  }
};

foo.integration.test.js

const request = require('supertest');
const httpStatus = require('http-status');
const sinon = require('sinon');
const mongoose = require('../../../database');

const deleteModule = module => delete require.cache[require.resolve(module)];

const requireUncached = module => {
  deleteModule(module);
  return require(module);
};

describe('Foo - Integration Test', async () => {
  describe('POST /v1/foo', () => {
    const fooService = require('../../services/foo');
    const stub = sinon.stub(fooService, 'service');
    let db;
    before(async () => {
      db = await mongoose.connect();
    });

    afterEach(async () => {
      sinon.restore();
    });

    after(async () => {
      await db.close();
    });

    it('the api should response successfully', async () => {
      stub.returns({});
      const payload = { task: 'task', payload: [{ pathParameters: {}, body: {} }] };
      const app = requireUncached('../../../app');
      await request(app)
        .post('/api/foo')
        .send(payload)
        .expect(httpStatus.OK);
    });

    it('the api should response with an error', async () => {
      stub.throwsException();
      const payload = { task: 'task', payload: [{ pathParameters: {}, body: {} }] };
      const app = requireUncached('../../../app');
      await request(app)
        .post('/api/foo')
        .send(payload)
        .expect(httpStatus.INTERNAL_SERVER_ERROR);
    });
  });
});

其他集成测试具有相同的结构。我也尝试过使用 proxyquire 但没有奏效。

我也尝试过成功删除 de app.js 的缓存。

有什么想法吗?

【问题讨论】:

    标签: node.js express integration-testing sinon supertest


    【解决方案1】:

    上下文:集成测试。

    1. 我同意您的想法:“测试所有组件是否以正确的方式连接”。那么你需要的是spy,而不是存根。当有case/condition时,需要设置preconfigured/dummy data(up mongodb with specific data),开启HTTP server,调用具体数据的HTTP request(post / get with specific query),检查HTTP响应正确的状态等。间谍需要检查/验证/验证您的服务是否以正确的参数调用并以正确的结果响应。此测试验证您已正确配置路由 - 控制器到特定 HTTP 请求的服务。
    2. 您一定有疑问:如何测试负面情景?例如:404, 500。那么你需要知道哪个具体场景做什么,哪个结果为否定条件。例如:如果request带有未知ID查询,则response为404。或者如果express未连接数据库,则response为500。您需要了解真实场景,并再次提供require设置以产生否定响应.
    3. 对于问题:“当我运行单个测试时,一切正常。问题是当我为不同的控制器运行多个单元测试时,存根在第二次运行中不起作用。”。有几种可能的解决方案,重点是:您必须确保正确准备特定场景/案例的条件。

    你可以这样做:

    • 创建sandbox,以确保在测试用例之间没有其他存根服务运行。
    • 在每个服务的测试运行之前启动新的 http(and 或 db)服务器并在测试运行之后关闭服务器,(例如:启动应用程序并使用真正的 http 客户端 - 作为 supertest 的替代方案)
    • 在调试模式下运行以找出为什么第二个存根不运行或不被调用或不工作,
    • 将实现从 stub 更改为 spy,您已经进行了单元测试,您只需要检查服务是否被调用,然后检查整体响应。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-26
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      相关资源
      最近更新 更多