Mongodb 带supertest的单元测试API,带链式函数的存根mongoose模型

Mongodb 带supertest的单元测试API,带链式函数的存根mongoose模型,mongodb,unit-testing,mongoose,sinon,supertest,Mongodb,Unit Testing,Mongoose,Sinon,Supertest,我正在尝试对Node.js+expressRESTAPI进行单元测试。这都是非常标准的CRUD内容,后端数据库库是MongoDB,用Mongoose调用。到目前为止,标准是 我想用它来测试我的API控制器/路由,所以我自然需要删除数据库调用 这正是我努力的地方,尤其是在查询结果上的.limit()和.skip()等链式调用 尝试用sinon存根mongoose DocumentQuery limit函数不起作用,例如 const stubQuery=sinon.stub(mongoose.Doc

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

我想用它来测试我的API控制器/路由,所以我自然需要删除数据库调用

这正是我努力的地方,尤其是在查询结果上的
.limit()
.skip()
等链式调用

尝试用sinon存根mongoose DocumentQuery limit函数不起作用,例如

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

正在尝试存根未定义的属性“limit”

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

我的测试是这样的

const-app=require('../server').app;
//stubout model.find()
const stubModelFind=sinon.stub(mongoose.Model,'find').callsFake(()=>{
return[{foo:bar/*假文档*/}]
})
描述('Thing API',()=>{
它('返回一些东西',(完成)=>{
请求(应用程序)
.get(“/api/things”)
.expect(函数(res){
expect(res.body.to.be.an.an('array'))
//我的其他测试/验证检查在这里
})
.预计(200,完成);
});
})
由于
This.model.find(…).limit不是一个函数,因为我的服务(即代码中通过Mongoose与DB对话的层)看起来有点像这样(其中
This.model
是Mongoose模型的一个实例)

let items=wait this.model.find(查询)
.限额
.跳过(跳过);

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

例如

server.ts

从“express”导入express;
从“./model”导入类别;
从“http”导入http;
常量app=express();
app.get('/api/things',异步(req,res)=>{
const items=等待类别
.find({name:'book'})
.限额(10)
.跳过(0);
res.json(项目);
});
const server=http.createServer(app).listen(3000,()=>{
console.info('Http服务器正在侦听http://localhost:3000');
});
导出{server};
model.ts

从“mongoose”导入{model,Schema};
const modelName='category';
const categorySchema=新模式(
{
名称:String,
父项:{type:Schema.Types.ObjectId,ref:modelName},
slug:{type:String},
祖先:[{u id:{type:Schema.Types.ObjectId,ref:modelName},name:String,slug:String}],
},
{
集合:“类别层次结构\类别”,
},
);
常量类别=模型(模型名称、类别模式);
导出默认类别;
server.integration.spec.ts

从“supertest”导入请求;
从“/server”导入{server};
从“chai”导入{expect};
从“sinon”进口sinon;
从“./model”导入类别;
之后((完成)=>{
服务器关闭(完成);
});
描述('Thing API',()=>{
它('返回一些东西',(完成)=>{
const limitStub=sinon.stub().returnsThis();
const skipsub=sinon.stub()。返回([{foo:'bar'}]);
sinon.stub(categories,'find').callsFake(():any=>{
返回{
限制:限制存根,
skip:skipsub,
};
});
请求(服务器)
.get(“/api/things”)
.expect((res)=>{
expect(res.body).to.be.an('array');
expect((categories.find as sinon.SinonStub);
expect(limitStub.calledWith(10))to.be.true;
expect(skipsub.calledWith(0)).to.be.true;
})
.预计(200,完成);
});
});
100%覆盖率的集成测试结果:

Http服务器正在侦听http://localhost:3000
事物API
✓ 返回一些东西
1次通过(43毫秒)
----------------------------|----------|----------|----------|----------|-------------------|
文件|%Stmts |%Branch |%Funcs |%Line |未覆盖行|s|
----------------------------|----------|----------|----------|----------|-------------------|
所有文件| 100 | 100 | 100 | 100 ||
型号:ts | 100 | 100 | 100 | 100 ||
server.integration.spec.ts | 100 | 100 | 100 | 100 ||
server.ts | 100 | 100 | 100 | 100 ||
----------------------------|----------|----------|----------|----------|-------------------|
源代码: