【问题标题】:unit test node.js mongoose mocha chai sinon单元测试node.js mongoose mocha chai sinon
【发布时间】:2018-11-05 21:02:06
【问题描述】:

我是单元测试的新手。我的 Web API 项目是基于 node-express-mongoose 的 MVC。

我有一个conroller.js,如下:

const getComments = async (req, res) => {
  let query = {};

  try {
    query = req.query.filter ? { email: new RegExp(`.*${req.query.filter}.*`, 'i') } : query;
    const comments = await util.getComments(query);
    return res.json(comments);
  } catch (err) {
    return res.status(500).send(`Internal server error: ${err}`);
  }
};

控制器使用实现所有数据库操作的util.js函数:

const comments = require('../models/comments');

exports.getComments = (query) => {
      try {
        return comments.find(query).sort({ createdAt: -1 });
      } catch (err) {
        throw err;
      }
};

如何使用 mocha & chai 创建单元测试?我是否必须使用 sinon 等创建假模拟?

【问题讨论】:

  • 您不能对数据库调用进行单元测试,您只能测试它具有查询对象的 getComments 方法。因为你必须模拟数据库模型响应。您必须使用 mongo-in-memory (npmjs.com/package/mongo-in-memory) 并模拟 mongodb。
  • 用sinon不能插入假数据?
  • 我的意思是它是可能的,但它不是单元测试。实际上,您正在测试 cmets 模型而不是 getComments 方法本身。这意味着您不测试自己的代码,您测试的是已经由 mongoose 贡献者测试的 mongoose 模型。
  • 我已经对虚拟内存数据库服务器的示例进行了更新,这对您的问题非常有帮助。但请确保模拟数据与您的模型中的数据相同
  • Tnx。我不明白为什么测试自己的方法不是“单元测试”?单元测试不包括数据库操作吗?如果代码中有类似的方法,应该如何进行单元测试?

标签: node.js unit-testing mongoose mocha.js sinon


【解决方案1】:

等等等等……

问题表明我们正在谈论“单元测试”,因此单元测试是我们评估函数/类/我们开发的任何内容的正确行为的那些。只是这个和其他任何东西。现在,太糟糕了,我不是 mongoDB 开发人员,也不是 mongo-memory-server 贡献者,所以我真的不需要在测试中考虑这些软件。这就是测试替身(存根/模拟/间谍)诞生的原因,作为优秀的软件工程师,我们应该明智地使用它们

所以,这是我的单元测试:

const {expect} = require("chai")
const sinon = require("sinon")

const uut = require("./users.service")

const UserModel = require("./user.model")

it("given correct userId should retrieve users full name" , async () => {

    //given
    const fixture = {
        _id : "fakeuser",
        name: "Fake",
        surname: "User"
    }

    let stub = sinon.stub(UserModel , "findById").returns(fixture)

    //when
    let result = await uut.getUserFullnameById(fixture._id)

    //then
    expect(result).to.eq("Fake User")
    stub.restore()

})

这个测试告诉我 getUserFullnameById 函数行为正确,

const User = require("./user.model")

module.exports.getUserFullnameById = async function (userId) {

    let user = await User.findById(userId)
    
    return `${user.name} ${user.surname}`
}

我将我的逻辑与 mongoose 隔离开来,因为我不需要知道 mongoose 是否工作并且我能够连接到底层的 mongodb 实例。所以有人可能会指出我的测试通过了,即使 mongoose 库中没有“findById”API,这也是我依赖集成测试的原因

describe("integration test" , () => {

    const mongoose = require("mongoose")
    

    before(()=> {
        mongoose.connect("mongodb://localhost:27017/db" , { useNewUrlParser: true , useUnifiedTopology: true})
        mongoose.Promise = global.Promise
    })

    beforeEach(async ()=> {
        await mongoose.connection.dropDatabase()
    })

    it("given correct userId should retrieve users full name" , async () => {

        let fixture  = new UserModel({name : "Fake" , surname : "User"})

        await fixture.save()

        let result = await uut.getUserFullnameById(fixture._id)

        expect(result).to.eq("Fake User")


    })
})

集成测试和之前做的一样,但它是在上下文中进行的。如果我弄乱了我的测试方法,我可能会破坏集成测试,我会知道我在滥用 mongoose 或 mongodb 连接,或者我可以同时破坏两者,所以我知道我可能只是不尊重我的业务规则。另一个优点:集成测试缓慢而脆弱,但我可以提供高质量的代码,甚至提供更少的代码并添加更多的独立单元测试

现在您还有两种情况:名字不存在和姓氏不存在:您如何测试和扩展您的代码?

【讨论】:

    【解决方案2】:

    如果我想在不模拟 db 的情况下对无法避免的方法进行测试,我使用 mongodb-memory-server 作为数据库并模拟 mongodb 行为。

    const Comment = require('../models/comments');
    
    const mockedComments = [ // modify example data depending on Your model
      {userId: "1", body: "Mocked comment of user 1", createdAt: Date.now() },
      {userId: "2", body: "Mocked comment of user 2", createdAt: Date.now() },
    ];
    
    const getComments = require('../path/to/getComments');
    
    
    const mongoose = require('mongoose');
    const MongodbMemoryServer = require('mongodb-memory-server');
    
    let mongoServer;
    const opts = { useMongoClient: true }; 
    
    before((done) => {
      mongoServer = new MongodbMemoryServer();
    
      mongoServer.getConnectionString()
        .then((mongoUri) => {
          return mongoose.connect(mongoUri, opts, (err) => {
            if (err) done(err);
          });
        })
        .then(() => {
         // preparing in memory database contents 
         Promise.all([
           Comment.create(mockedComments[0]),
           Comment.create(mockedComments[1])
         ]).then(() => done());
        });
    });
    
    after(() => {
      mongoose.disconnect();
      mongoServer.stop();
    });
    
    describe('getComments', () => {
    
      it('successfully returns comments', async () => {
        const result = await getComments({});
        expect(result.length).to.equal(mockedComments.length);
      });
    
      it('successfully returns comments of user', async () => {
        const result = await getComments({userId: 1});
        expect(result[0].userId).to.equal(1);
      });
    });
    

    【讨论】:

      猜你喜欢
      • 2023-04-11
      • 2015-06-02
      • 2021-09-27
      • 2019-02-03
      • 2017-06-09
      • 2015-12-01
      • 2018-04-08
      • 2018-02-13
      • 2016-05-26
      相关资源
      最近更新 更多