在 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()
  });