具有超级测试的单元测试API,具有链接功能的猫鼬模型存根

我正在尝试对Node.js + Express REST API进行单元测试。这些都是非常标准的CRUD内容,后端数据库库是MongoDB与Mongoose一起调用。到目前为止,如此标准...

我想使用SuperTest测试我的API控制器/路由,因此自然需要对数据库调用进行存根处理

这就是我在努力的地方,尤其是对于查询结果中的.limit().skip()之类的链接调用。

尝试用sinon对猫鼬DocumentQuery限制函数进行存根操作无效,例如

const stubQuery = sinon.stub(mongoose.DocumentQuery,'limit')
.callsFake(()=>{})

导致错误

  

尝试对未定义的属性“ limit”进行存根

我尝试使用sinon-mongoose库,但是在SuperTest中我的API调用上下文中无法使用它。似乎只用于直接在我没有做的模型上运行测试

我的测试看起来像这样

const app = require('../server').app;

// stub out model.find()
const stubModelFind = sinon.stub(mongoose.Model,'find').callsFake(() => {
  return [ { foo: "bar" /* fake documents */ } ]
})

describe('Thing API',() => {
  it('returns some things',(done) => {
    request(app)
      .get('/api/things')
      .expect(function(res) {
        expect(res.body).to.be.an.an('array')
        // Rest of my tests / validation checks here
      })
      .expect(200,done);
  });
})

该测试由于this.model.find(...).limit is not a function而失败,因为我的服务(这是我的代码中通过Mongoose与DB进行通信的层)看起来像这样(其中this.model是Mongoose模型的实例)

let items = await this.model.find(query)
  .limit(limit)
  .skip(skip);
mengyingyu2009 回答:具有超级测试的单元测试API,具有链接功能的猫鼬模型存根

这是集成测试解决方案,为了存根链方法,您需要使用stub.returnsThis();

例如

server.ts

import express from 'express';
import categories from './model';
import http from 'http';

const app = express();
app.get('/api/things',async (req,res) => {
  const items = await categories
    .find({ name: 'book' })
    .limit(10)
    .skip(0);
  res.json(items);
});

const server = http.createServer(app).listen(3000,() => {
  console.info('Http server is listening on http://localhost:3000');
});

export { server };

model.ts

import { model,Schema } from 'mongoose';

const modelName = 'category';

const categorySchema = new Schema(
  {
    name: String,parent: { type: Schema.Types.ObjectId,ref: modelName },slug: { type: String },ancestors: [{ _id: { type: Schema.Types.ObjectId,name: String,slug: String }],},{
    collection: 'category-hierarchy_categories',);

const categories = model(modelName,categorySchema);

export default categories;

server.integration.spec.ts

import request from 'supertest';
import { server } from './server';
import { expect } from 'chai';
import sinon from 'sinon';
import categories from './model';

after((done) => {
  server.close(done);
});

describe('Thing API',() => {
  it('returns some things',(done) => {
    const limitStub = sinon.stub().returnsThis();
    const skipStub = sinon.stub().returns([{ foo: 'bar' }]);
    sinon.stub(categories,'find').callsFake((): any => {
      return {
        limit: limitStub,skip: skipStub,};
    });

    request(server)
      .get('/api/things')
      .expect((res) => {
        expect(res.body).to.be.an('array');
        expect((categories.find as sinon.SinonStub).calledWith({ name: 'book' })).to.be.true;
        expect(limitStub.calledWith(10)).to.be.true;
        expect(skipStub.calledWith(0)).to.be.true;
      })
      .expect(200,done);
  });
});

覆盖率100%的集成测试结果:

Http server is listening on http://localhost:3000
  Thing API
    ✓ returns some things


  1 passing (43ms)

----------------------------|----------|----------|----------|----------|-------------------|
File                        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------------------------|----------|----------|----------|----------|-------------------|
All files                   |      100 |      100 |      100 |      100 |                   |
 model.ts                   |      100 |      100 |      100 |      100 |                   |
 server.integration.spec.ts |      100 |      100 |      100 |      100 |                   |
 server.ts                  |      100 |      100 |      100 |      100 |                   |
----------------------------|----------|----------|----------|----------|-------------------|

源代码:https://github.com/mrdulin/mongoose5.x-lab/tree/master/src/stackoverflow/58787092

本文链接:https://www.f2er.com/3129890.html

大家都在问