在 mocha 测试中的 for 循环中进行顺序猫鼬查询
Making sequential mongoose queries in a for loop inside a mocha test
我正在尝试使用 mocha 作为我的 API 应用程序的测试驱动程序。
我有一个 json 的用户名到歌曲名称,我正试图将其提供给 API 端点。
json:
{
"description": "hold the lists that each user heard before",
"userIds":{
"a": ["m2","m6"],
"b": ["m4","m9"],
"c": ["m8","m7"],
"d": ["m2","m6","m7"],
"e": ["m11"]
}
}
假设 User 和 Song 架构只是带有名称的 _id。我想要做的是在解析 json 文件后,通过按名称对 "a" 进行猫鼬查找,将 "a" 转换为 user_id,假设 id=0。将每个 "m2" 按名称转换为 song_id 让我们说 id=1,然后调用
请求,http://localhost:3001/listen/0/1
这是我到目前为止的 mocha 测试:
test.js:
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
for (var userName in parsedListenJSON) {
if (parsedListenJSON.hasOwnProperty(userName)) {
var songs = parsedListenJSON[userName];
var currentUser;
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
currentUser = user;
});
for (var i=0; i<songs.length; i++)
{
//console.log(songs[i]);
var songQuery = Song.findOne({'name': songs[i]})
songQuery.then(function(song){
console.log("user_id: " + currentUser["_id"]);
console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + currentUser["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(err, res){
expect(res.status).to.equal(200);
});
});
}
}
}
done();
})
问题:
1) 在进行 API 校准之前,我需要确保同时拥有 user_id 和 song_id。现在,每个 User.findOne 和 Song.findOne 查询都有自己的 then 子句。我将 user_id 通过 currentUser 变量传递到 Song 查询的 then 块中。但是由于查询是异步的,我认为这是不合适的。我如何构造此代码,以便在两个 then 块都已执行时继续 API 调用。
2) 当我按原样 运行 代码时,只有第一个用户被执行,而不是其余的,
IE。打印出来的是:
user_id: 0
song_id: 1
http://localhost:3001/listen/0/1
user_id: 0
song_id: 5
http://localhost:3001/listen/0/5
3) API 端点与邮递员一起工作,并在下面的更简单的 Mocha 测试中使用。但它似乎不适用于我的原始代码。
var request = require('superagent');
var expect = require('expect.js');
var User = require('../models/user');
var Song = require('../models/song');
var mongoose = require('mongoose');
mongoose.connect('localhost/TestSongRecommender');
...
it('adds the songs', function(){
request
.post('http://localhost:3001/listen/0/2')
.end(function(res){
expect(res.status).to.equal(200);
});
});
更新:
async.forEach 方法有效。这是我的最后一个片段:
已更新test.js
var request = require('superagent');
var expect = require('expect.js');
var async = require('async');
var User = require('../models/user');
var Song = require('../models/song');
var mongoose = require('mongoose');
mongoose.connect('localhost/Test');
describe('song recommendations', function(){
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
async.forEach(Object.keys(parsedListenJSON), function forAllUsers(userName, callback) {
var songs = parsedListenJSON[userName];
//var currentUser;
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
//console.log(songs);
//currentUser = user;
async.forEach(songs, function runSongQuery(songName, smallback) {
//console.log(songName);
var songQuery = Song.findOne({'name': songName})
songQuery.then(function(song){
//console.log("user_id: " + user["_id"]);
//console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + user["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(err, res){
expect(res.status).to.equal(200);
smallback()
});
});
}, function allSongs(err) {
callback();
})
});
}, function allUserNames(err) {
done()
})
})
});
1&2) 您将要使用 NPM 中的异步包。您可以使用主文件夹中的命令 npm install async --save
和代码中的 var async = require('async')
命令获取它。
您必须将每个 for 循环替换为 async.forEach。 async.forEach 接受一个数组和两个函数作为参数。一旦所有回调都已 returned.
,它会调用数组中每个项目的第一个函数和第二个函数
您目前使用 for 循环的方式行不通。到异步事物 return 时,您的循环已经遍历它们(非阻塞 IO,记住)并且您的变量不再正确设置。
您还需要将事情设置为仅在所有代码都具有 运行.
后才调用 done
如果写得好,最终会是这样的:
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
async.forEach(parsedListenJSON, function forAllUsers(userName, callback) {
var songs = parsedListenJSON[userName];
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
var currentUser = user;
async.forEach(songs, function runSongQuery(songName, smallback) {
var songQuery = Song.findOne({'name': songName})
songQuery.then(function(song){
console.log("user_id: " + currentUser["_id"]);
console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + currentUser["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(res){
expect(res.status).to.equal(200);
smallback()
});
});
}, function allRan(err) {
callback();
})
});
}, function allUserNames(err) {
done()
})
})
3) 您将要使用测试框架来测试您的 API。我推荐supertest。拥有超级测试后,需要您的应用程序和超级测试:
var request = require('supertest');
var app = require('./testApp.js');
在以下测试中使用它:
request(app)
.post(api_request)
.end(function(res){
expect(res.status).to.equal(200);
smallback()
});
我正在尝试使用 mocha 作为我的 API 应用程序的测试驱动程序。
我有一个 json 的用户名到歌曲名称,我正试图将其提供给 API 端点。
json:
{
"description": "hold the lists that each user heard before",
"userIds":{
"a": ["m2","m6"],
"b": ["m4","m9"],
"c": ["m8","m7"],
"d": ["m2","m6","m7"],
"e": ["m11"]
}
}
假设 User 和 Song 架构只是带有名称的 _id。我想要做的是在解析 json 文件后,通过按名称对 "a" 进行猫鼬查找,将 "a" 转换为 user_id,假设 id=0。将每个 "m2" 按名称转换为 song_id 让我们说 id=1,然后调用 请求,http://localhost:3001/listen/0/1
这是我到目前为止的 mocha 测试:
test.js:
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
for (var userName in parsedListenJSON) {
if (parsedListenJSON.hasOwnProperty(userName)) {
var songs = parsedListenJSON[userName];
var currentUser;
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
currentUser = user;
});
for (var i=0; i<songs.length; i++)
{
//console.log(songs[i]);
var songQuery = Song.findOne({'name': songs[i]})
songQuery.then(function(song){
console.log("user_id: " + currentUser["_id"]);
console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + currentUser["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(err, res){
expect(res.status).to.equal(200);
});
});
}
}
}
done();
})
问题:
1) 在进行 API 校准之前,我需要确保同时拥有 user_id 和 song_id。现在,每个 User.findOne 和 Song.findOne 查询都有自己的 then 子句。我将 user_id 通过 currentUser 变量传递到 Song 查询的 then 块中。但是由于查询是异步的,我认为这是不合适的。我如何构造此代码,以便在两个 then 块都已执行时继续 API 调用。
2) 当我按原样 运行 代码时,只有第一个用户被执行,而不是其余的, IE。打印出来的是: user_id: 0 song_id: 1 http://localhost:3001/listen/0/1 user_id: 0 song_id: 5 http://localhost:3001/listen/0/5
3) API 端点与邮递员一起工作,并在下面的更简单的 Mocha 测试中使用。但它似乎不适用于我的原始代码。
var request = require('superagent');
var expect = require('expect.js');
var User = require('../models/user');
var Song = require('../models/song');
var mongoose = require('mongoose');
mongoose.connect('localhost/TestSongRecommender');
...
it('adds the songs', function(){
request
.post('http://localhost:3001/listen/0/2')
.end(function(res){
expect(res.status).to.equal(200);
});
});
更新:
async.forEach 方法有效。这是我的最后一个片段:
已更新test.js
var request = require('superagent');
var expect = require('expect.js');
var async = require('async');
var User = require('../models/user');
var Song = require('../models/song');
var mongoose = require('mongoose');
mongoose.connect('localhost/Test');
describe('song recommendations', function(){
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
async.forEach(Object.keys(parsedListenJSON), function forAllUsers(userName, callback) {
var songs = parsedListenJSON[userName];
//var currentUser;
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
//console.log(songs);
//currentUser = user;
async.forEach(songs, function runSongQuery(songName, smallback) {
//console.log(songName);
var songQuery = Song.findOne({'name': songName})
songQuery.then(function(song){
//console.log("user_id: " + user["_id"]);
//console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + user["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(err, res){
expect(res.status).to.equal(200);
smallback()
});
});
}, function allSongs(err) {
callback();
})
});
}, function allUserNames(err) {
done()
})
})
});
1&2) 您将要使用 NPM 中的异步包。您可以使用主文件夹中的命令 npm install async --save
和代码中的 var async = require('async')
命令获取它。
您必须将每个 for 循环替换为 async.forEach。 async.forEach 接受一个数组和两个函数作为参数。一旦所有回调都已 returned.
,它会调用数组中每个项目的第一个函数和第二个函数您目前使用 for 循环的方式行不通。到异步事物 return 时,您的循环已经遍历它们(非阻塞 IO,记住)并且您的变量不再正确设置。
您还需要将事情设置为仅在所有代码都具有 运行.
后才调用 done如果写得好,最终会是这样的:
it('adds the songs the users listen to', function(done){
var parsedListenJSON = require('../listen.json')["userIds"];
//console.log(parsedListenJSON);
async.forEach(parsedListenJSON, function forAllUsers(userName, callback) {
var songs = parsedListenJSON[userName];
//console.log(userName + " -> " + songs);
var userQuery = User.findOne({'name': userName})
userQuery.then(function(user){
//console.log("user_id: " + user["_id"]);
var currentUser = user;
async.forEach(songs, function runSongQuery(songName, smallback) {
var songQuery = Song.findOne({'name': songName})
songQuery.then(function(song){
console.log("user_id: " + currentUser["_id"]);
console.log("song_id: " + song["_id"]);
// need to ensure we have both the user_id and song_id
api_request = "http://localhost:3001/listen/" + currentUser["_id"] + "/" + song["_id"];
console.log(api_request);
// listen/user_id/song_id
//.post('http://localhost:3001/listen/0/1')
request
.post(api_request)
.end(function(res){
expect(res.status).to.equal(200);
smallback()
});
});
}, function allRan(err) {
callback();
})
});
}, function allUserNames(err) {
done()
})
})
3) 您将要使用测试框架来测试您的 API。我推荐supertest。拥有超级测试后,需要您的应用程序和超级测试:
var request = require('supertest');
var app = require('./testApp.js');
在以下测试中使用它:
request(app)
.post(api_request)
.end(function(res){
expect(res.status).to.equal(200);
smallback()
});