Node.js 如何在集成测试中使用Sinon存根服务?

Node.js 如何在集成测试中使用Sinon存根服务?,node.js,express,integration-testing,sinon,supertest,Node.js,Express,Integration Testing,Sinon,Supertest,我正试图在express中为我的api做一些集成测试 我的API的结构类似于: app -> routes -> controllers -> services 因为我已经有了单元测试,所以我的想法是只测试所有组件是否以正确的方式连接 因此,我的想法是使用Sinon为服务创建一个存根,并且只使用supertest检查控制器的响应 当我运行一个测试时,一切都正常。问题是,当我为不同的控制器运行多个单元测试时,存根在第二次运行时不起作用 我认为这是因为该应用程序已经作为一个模块保

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

我的API的结构类似于:

app -> routes -> controllers -> services
因为我已经有了单元测试,所以我的想法是只测试所有组件是否以正确的方式连接

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

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

我认为这是因为该应用程序已经作为一个模块保存在缓存中,所以sinon无法存根该服务

我的代码的一些示例:

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的缓存,但没有成功


有什么想法吗?

上下文:集成测试

  • 我同意你的想法:“测试所有组件是否以正确的方式连接”。那么您需要的是间谍,而不是存根。当出现案例/情况时,您需要设置预配置/虚拟数据(使用特定数据启动mongodb),打开HTTP服务器,使用特定数据调用HTTP请求(使用特定查询post/get),并检查HTTP响应的正确状态,间谍需要检查/验证/验证是否使用正确的参数调用了您的服务,并以正确的结果响应。此测试验证您是否为特定HTTP请求正确配置了服务的路由控制器
  • 你们一定有问题:如何测试负面情景?例如:404500。然后你需要知道哪种特定的情况下做什么,哪种结果是消极的情况。例如:若请求带有未知ID查询,那个么响应将是404。或者,如果express未连接到数据库,则响应将为500。您需要了解真实场景,并再次提供require设置以生成否定响应
  • 对于问题:“当我运行单个测试时,一切都正常。问题是当我为不同的控制器运行多个单元测试时,存根在第二次运行时不工作。”。有几种可能的解决方案,要点是:您必须确保正确准备特定场景/案例的条件 你可以做:

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