如何在Node.js应用程序中对控制器进行单元测试?

3

我正在使用fastify框架和sequelize作为ORM来开发我的node.js应用程序。我使用Mocha、Chai和Sinon进行单元测试。我需要对我的控制器函数进行单元测试。以下是示例控制器函数。

// controllers.js;

const service = require('../services');

exports.create = (req, reply) => {
  const attributes = req.body;
  service.create(attributes)
    .then((result) => {
      reply.code(201).send(result);
    })
    .catch((error) => {
      reply.send(error);
    });
};

我的服务文件如下:
// services.js;

const { Model } = require('../models');

function create(attributes) {
  return Model.create(attributes);
}

module.exports = { create };


在上述代码中,我想仅对controllers.js中的“create”函数进行单元测试。但问题是,由于正在进行单元测试,因此它不应该调用数据库。但是,在service.js文件中的Model.create将调用数据库。我如何仅对控制器函数进行单元测试?
1个回答

2
你应该桩接口中的service.create方法,并创建模拟的reqreply对象。

例如:

controller.js:

const service = require('./service');

exports.create = (req, reply) => {
  const attributes = req.body;
  service
    .create(attributes)
    .then((result) => {
      reply.code(201).send(result);
    })
    .catch((error) => {
      reply.send(error);
    });
};

service.js:

const { Model } = require('./models');

function create(attributes) {
  return Model.create(attributes);
}

module.exports = { create };

models.js:

const Model = {
  create() {
    console.log('real implementation');
  },
};

module.exports = { Model };

controller.test.js:

const controller = require('./controller');
const service = require('./service');
const sinon = require('sinon');

const flushPromises = () => new Promise(setImmediate);

describe('62536251', () => {
  afterEach(() => {
    sinon.restore();
  });
  it('should create', async () => {
    const mResult = 'success';
    sinon.stub(service, 'create').resolves(mResult);
    const mReq = { body: {} };
    const mReply = { code: sinon.stub().returnsThis(), send: sinon.stub() };
    controller.create(mReq, mReply);
    await flushPromises();
    sinon.assert.calledWith(mReply.code, 201);
    sinon.assert.calledWith(mReply.send, 'success');
  });

  it('should handle error', async () => {
    const mError = new Error('network');
    sinon.stub(service, 'create').rejects(mError);
    const mReq = { body: {} };
    const mReply = { code: sinon.stub().returnsThis(), send: sinon.stub() };
    controller.create(mReq, mReply);
    await flushPromises();
    sinon.assert.calledWith(mReply.send, mError);
  });
});

单元测试结果与覆盖率报告:

  62536251
    ✓ should create
    ✓ should handle error


  2 passing (13ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   83.33 |      100 |      60 |   83.33 |                   
 controller.js |     100 |      100 |     100 |     100 |                   
 models.js     |   66.67 |      100 |       0 |   66.67 | 3                 
 service.js    |   66.67 |      100 |       0 |   66.67 | 4                 
---------------|---------|----------|---------|---------|-------------------

谢谢@slideshowp2。它起作用了,但有一件事是它要求数据库URL。我认为由于我在服务文件中需要模型,所以它要求数据库URL。我应该如何存根或模拟在服务中所需的这些模型? - Alex Isaac
它显示错误为TypeError [ERR_INVALID_ARG_TYPE]:参数"url"必须是字符串类型。只有在终端输入以下内容后,测试才能成功运行 export DATABASE_URL=postgres://username:password@localhost:5432/databasename。那么这可以被视为单元测试吗? - Alex Isaac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接