Nodejs Expressjs Mongodb Javascript 使用 Mocha Chai Sinon 对随机 Nodejs 项目进行单元测试

Nodejs Expressjs Mongodb Javascript Unit Test Using Mocha Chai Sinon for random Nodejs Project

有人可以告诉我如何为函数 homeDog 的示例项目执行“单元测试”吗? 我有以下示例函数,我希望尝试为它添加单元测试,同时我学习如何使用 Mocha Chai Sinon 进行单元测试,示例随机 Nodejs 项目引用来自 https://github.com/seanaharrison/node-express-mongodb-example.

我一直在努力为 homeDog 功能进行单元测试,但后来我遇到了问题,有人可以向我展示一个工作单元测试,告诉我如何为功能 homeDog 进行单元测试,让我有一个起点吗?

这是我尝试但失败的方法。

exports.homeDog = function(req, res) {
    var db = req.db;
    var collection = db.collection('dogs');
    collection.find().toArray(function(err, dogsArray) {
        if (dogsArray) {
            res.render('index', {
                title: 'Dogs',
                path: req.path,
                dogs: dogsArray
            });
        }
        else {
            res.render('index', {
                title: 'No Dogs Found'
            });
        }
    });
};

由于您需要一个“模拟数据库”,但您没有在代码中使用它(我认为),我将尝试解释如何使用 mock database 测试 API 项目.

即:您不需要真正的数据库来测试您的 API,您需要在每次执行测试时创建和关闭“内存中的数据库”。

首先是安装模拟依赖项,如 mongo-mock, mongodb-memory-server 或任何你想要的。

就个人而言,我使用 mongodb-memory-server

所以按照文档你可以设置你的项目。我的 mockDB.js 文件与 test.js.

处于同一级别

mockDB.js文件应包含如下内容(如果您使用其他包,此代码可能会更改。):

const { MongoMemoryServer } = require('mongodb-memory-server');
const mongod = new MongoMemoryServer();

module.exports.connect = async () => {
    const uri = await mongod.getUri();

    const mongooseOpts = {
        useNewUrlParser: true,
        useFindAndModify: false,
        useUnifiedTopology: true 
    };

    await mongoose.connect(uri, mongooseOpts);
}

然后,您将获得一个文件,您的 mock DB 将在其中进行初始化和连接。 至此,我的mongo DB就初始化好了。我正在使用 mongoose,但允许使用另一个选项。

控制器(我认为在本例中为 homeDog)文件在这里无关紧要,因为您正在测试控制器,因此代码必须与生产中的代码相同。 控制器是要测试的代码,因此不应出于测试目的对其进行修改。

最后一个文件是test.js。 在此文件中,您必须导入 mockDB.js 文件并启动数据库。

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

... 

before(async () => await mockDB.connect());

通过这种方式,您可以执行测试,控制器将执行对 memory database 的查询。此外,您可以使用您的文件 mockDB.js 来实现辅助查询。

例如,要从字段中获取特定值,您可以创建类似

的方法
module.exports.findValueById(id) => {
  //return result
}

并在您的测试文件中调用此模块:

var idFound = mockDB.findValueById(id)

例如,您可以使用它在插入文档后查询数据库并检查集合是否正常。或者检查更新是否正确或任何你想要的。

如果您的函数是 GET,您只需将 homeDog 返回的数据与现有的“模拟 DB”进行比较。

你提到了单元测试,这意味着它应该是一个孤立的测试(没有真正调用数据库)。为此,您需要包含 Sinon 以模拟某些函数。

一些要模拟的函数:

  1. db.collection
  2. collection.find().toArray()
  3. res.render
// include the sinon library
const sinon = require('sinon');

// include the source file that contain `homeDog` function
const src = require('./src');

describe('test', () => {
  it('returns index with title, path and dogs if dogs array exist', () => {
    const dogs = ['doggy', 'dogg2'];

    // define mock `req` parameter
    const mockReq = {
      path: 'whatever',
      db: {        
        collection: sinon.stub().returnsThis(), // `returnsThis` to mock chained function
        find: sinon.stub().returnsThis(),
        toArray: sinon.stub().callsFake(callback => {
          callback(null, dogs) // specify dogs array here
        })
      }
    };

    // define mock `res` parameter
    const mockRes = {
      render: sinon.stub(),
    }

    src.homeDog(mockReq, mockRes); // pass the mock req and mock res to homeDog

    // check whether the function is called correctly
    sinon.assert.calledWith(mockReq.db.collection, 'dogs');
    sinon.assert.calledWith(mockRes.render, 'index', { title: 'Dogs', path: 'whatever', dogs });
  })

  it('returns index with title', () => {
    const mockReq = {
      path: 'whatever',
      db: {        
        collection: sinon.stub().returnsThis(),
        find: sinon.stub().returnsThis(),
        toArray: sinon.stub().callsFake(callback => {
          callback(null, null) // give null value for dog array
        })
      }
    };

    const mockRes = {
      render: sinon.stub(),
    }

    src.homeDog(mockReq, mockRes); 

    sinon.assert.calledWith(mockRes.render, 'index', { title: 'No Dogs Found' });
  })
})

结果

  test
    ✓ returns index with title, path and dogs if dogs array exist
    ✓ returns index with title


  2 passing (12ms)