Node.js 在数组上迭代时进行Supertest双重回调

Node.js 在数组上迭代时进行Supertest双重回调,node.js,express,mocha.js,supertest,Node.js,Express,Mocha.js,Supertest,作为一个学习练习,我正在尝试使用Node和Express获得一个版本的running,我发现了一个特殊的问题。我可以从比我更了解Node和expression的人那里得到一些帮助来理解为什么 在尝试使strike测试用例工作时编写strike测试用例之后,当我尝试在命令行使用mocha运行以下测试时,我得到以下supertest错误:“Uncaught TypeError:undefined不是函数”在/Users/cdurfee/bitbucket/neontapir/node_modules

作为一个学习练习,我正在尝试使用Node和Express获得一个版本的running,我发现了一个特殊的问题。我可以从比我更了解Node和expression的人那里得到一些帮助来理解为什么

在尝试使strike测试用例工作时编写strike测试用例之后,当我尝试在命令行使用mocha运行以下测试时,我得到以下supertest错误:
“Uncaught TypeError:undefined不是函数”在/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/lib/test.js:125:21

然而,如果我在game.js(
total+=rolls[ball+2];
)中注释掉这句看似无害的话,就不会有失败,但行为当然是错误的。我怀疑这是一个数组越界的问题,但我不知道一个好方法来解决这个问题

以下是文件和mocha控制台输出的全部内容

08:55 $ mocha --reporter spec

  Scoring a bowling game
    gutter game
      ✓ should return 0 
    single pin game
      ✓ should return 20 
    spare
      ✓ should return 16 after spare and a 3 
    strike
      ✓ should return 24 after strike, 4 and a 3 
      1) should return 24 after strike, 4 and a 3
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
double callback!
    perfect game

  4 passing (71ms)
  1 failing

  1) Scoring a bowling game strike should return 24 after strike, 4 and a 3:
     Uncaught TypeError: undefined is not a function
      at /Users/cdurfee/bitbucket/neontapir/node_modules/supertest/lib/test.js:125:21
      at Test.Request.callback (/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/node_modules/superagent/lib/node/index.js:660:30)
      at ClientRequest.<anonymous> (/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/node_modules/superagent/lib/node/index.js:628:10)
      at ClientRequest.EventEmitter.emit (events.js:95:17)
      at Socket.socketErrorListener (http.js:1547:9)
      at Socket.EventEmitter.emit (events.js:95:17)
      at net.js:441:14
      at process._tickCallback (node.js:415:13)
08:55$mocha——报告者规范
在保龄球比赛中得分
水沟游戏
✓ 应该返回0
单脚游戏
✓ 应该返回20
备用的
✓ 应在备用和3后返回16
罢工
✓ 应该在罢工后24小时,4小时和3小时返回
1) 应该在罢工后24小时,4小时和3小时返回
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
双重回拨!
完美游戏
4次通过(71毫秒)
1失败
1) 在保龄球比赛中得分得分得分应在得分24后返回,4分和3分:
未捕获类型错误:未定义不是函数
at/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/lib/test.js:125:21
在Test.Request.callback(/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/node_modules/superagent/lib/node/index.js:660:30)
在ClientRequest。(/Users/cdurfee/bitbucket/neontapir/node_modules/supertest/node_modules/superagent/lib/node/index.js:628:10)
在ClientRequest.EventEmitter.emit(events.js:95:17)
位于Socket.socketErrorListener(http.js:1547:9)
位于Socket.EventEmitter.emit(events.js:95:17)
net.js:441:14
在进程中调用(node.js:415:13)
game.js

var express = require('express');
var app = exports.app = express();

app.get('/start', function(req, res) {
    rolls = new Array();
    attempt = 0;
});

app.post('/bowl/:pins', function(req, res) {
    rolls[attempt] = parseInt(req.params.pins);
    attempt++;
});

app.get('/score', function(req, res) {
    var total = 0;
    var ball = 0;
    for (var frame = 0; frame < 10; frame++) {
        if (rolls[ball] + rolls[ball + 1] == 10) { 
            total += rolls[ball + 2]; // this line causes the double callback
        }
        total += rolls[ball] + rolls[ball + 1];
        ball += 2;
    }

    res.send(200, {score: total});
});

app.listen(process.env.PORT || 3000);
var request = require('supertest'),
    should = require('should');
var game = require('../game.js').app;

var assertScoreEquals = function(expectedScore) {   
    request(game).get('/score').expect(200).end(function(err,res) {
      should.not.exist(err);        
      result = res.body;        
      result.should.have.property('score').eql(expectedScore);      
    });  
};

var roll = function(pins) {
    request(game).post('/bowl/' + pins).end();
};

var rollMany = function(times, pins) {
    for (var i = 0; i < times; i++) {
        roll(pins);
    }
};

describe('Scoring a bowling game', function() {
  beforeEach(function() {
    request(game).get('/start').end();
  });

  describe('gutter game', function() {
        it('should return 0', function() {
            rollMany(20,0);
            assertScoreEquals(0);                       
        });
    });

    describe('single pin game', function() {
        it('should return 20', function() {
            rollMany(20,1);
            assertScoreEquals(20);          
        });
    });

    describe('spare', function() {
        it('should return 16 after spare and a 3', function() {         
            roll(6);
            roll(4); // spare
            roll(3);
            rollMany(17,0);
            assertScoreEquals(16);          
        });
    });

    // not expected to pass at the moment
    describe('strike', function() {
        it('should return 24 after strike, 4 and a 3', function() {         
            roll(10); // strike
            roll(4);
            roll(3);
            rollMany(17,0);
            assertScoreEquals(24);          
        });
    });

    // not expected to pass at the moment
    describe('perfect game', function() {
        it('should return 300', function() {    
            rollMany(12,10);
            assertScoreEquals(300);         
        });
    });         
});
var express=require('express');
var app=exports.app=express();
app.get('/start',函数(req,res){
rolls=新数组();
尝试=0;
});
app.post('/BOLL/:pins',功能(要求、回复){
转鼓[尝试]=parseInt(要求参数引脚);
尝试++;
});
应用程序获取('/score',函数(req,res){
var合计=0;
var-ball=0;
用于(变量帧=0;帧<10;帧++){
如果(掷[球]+掷[球+1]==10){
总计+=转鼓[ball+2];//此行导致双重回调
}
总数+=转鼓[球]+转鼓[球+1];
球+=2;
}
res.send(200,{总分});
});
app.listen(process.env.PORT | | 3000);
test/test.js

var express = require('express');
var app = exports.app = express();

app.get('/start', function(req, res) {
    rolls = new Array();
    attempt = 0;
});

app.post('/bowl/:pins', function(req, res) {
    rolls[attempt] = parseInt(req.params.pins);
    attempt++;
});

app.get('/score', function(req, res) {
    var total = 0;
    var ball = 0;
    for (var frame = 0; frame < 10; frame++) {
        if (rolls[ball] + rolls[ball + 1] == 10) { 
            total += rolls[ball + 2]; // this line causes the double callback
        }
        total += rolls[ball] + rolls[ball + 1];
        ball += 2;
    }

    res.send(200, {score: total});
});

app.listen(process.env.PORT || 3000);
var request = require('supertest'),
    should = require('should');
var game = require('../game.js').app;

var assertScoreEquals = function(expectedScore) {   
    request(game).get('/score').expect(200).end(function(err,res) {
      should.not.exist(err);        
      result = res.body;        
      result.should.have.property('score').eql(expectedScore);      
    });  
};

var roll = function(pins) {
    request(game).post('/bowl/' + pins).end();
};

var rollMany = function(times, pins) {
    for (var i = 0; i < times; i++) {
        roll(pins);
    }
};

describe('Scoring a bowling game', function() {
  beforeEach(function() {
    request(game).get('/start').end();
  });

  describe('gutter game', function() {
        it('should return 0', function() {
            rollMany(20,0);
            assertScoreEquals(0);                       
        });
    });

    describe('single pin game', function() {
        it('should return 20', function() {
            rollMany(20,1);
            assertScoreEquals(20);          
        });
    });

    describe('spare', function() {
        it('should return 16 after spare and a 3', function() {         
            roll(6);
            roll(4); // spare
            roll(3);
            rollMany(17,0);
            assertScoreEquals(16);          
        });
    });

    // not expected to pass at the moment
    describe('strike', function() {
        it('should return 24 after strike, 4 and a 3', function() {         
            roll(10); // strike
            roll(4);
            roll(3);
            rollMany(17,0);
            assertScoreEquals(24);          
        });
    });

    // not expected to pass at the moment
    describe('perfect game', function() {
        it('should return 300', function() {    
            rollMany(12,10);
            assertScoreEquals(300);         
        });
    });         
});
var request=require('supertest'),
应该=需要('should');
var game=require('../game.js').app;
var assertScoreEquals=函数(expectedScore){
请求(游戏).get('/score').expect(200).end(函数(err,res){
不应该存在(错误);
结果=res.body;
结果.should.have.property('score').eql(expectedScore);
});  
};
var roll=功能(引脚){
请求(游戏).post('/bowl/'+pins.end();
};
var rollMany=函数(次数、管脚){
对于(变量i=0;i<次;i++){
辊(销);
}
};
描述(‘保龄球得分’功能(){
beforeach(函数(){
请求(游戏).get('/start').end();
});
描述('排水沟游戏',函数(){
它('应该返回0',函数(){
罗曼(20,0);
资产价值等于(0);
});
});
描述('单针游戏',功能(){
它('应该返回20',函数(){
rollMany(20,1);
资产净值等于(20);
});
});
描述('spare',function()){
它('应在备用后返回16,并返回一个3',函数(){
卷(6);
卷(4);//备用
卷(3);
罗曼(17,0);
资产负债表(16);
});
});
//目前预计不会通过
描述('strike',function()){
它('应该在strike之后返回24,4和3',函数(){
掷(10);//击
卷(4);
卷(3);
罗曼(17,0);
资产净值等于(24);
});
});
//目前预计不会通过
描述('完美游戏',函数(){
它('应该返回300',函数(){
罗曼(12,10);
资产价值等于(300);
});
});         
});
添加到我的

我还看到您的测试用例不是异步的,这几乎肯定是您的问题

您应该有一个回调:
done

  describe('gutter game', function() {
    it('should return 0', function(done) { // callback is provided as argument by mocha 
        rollMany(20,0);
        assertScoreEquals(0);
        done();  // This needs to be called when the test is finished, for async operations.
    });
});
我建议使用而不是supertest,因为当您需要运行大量请求时,这可以使事情变得更容易。这个库,再加上可能会使像您这样的测试更简单


我用
Bluebird
承诺和
承诺的supertest重写了您的测试,并对我所做的更改进行了评论。

我看到的一个主要问题是,您假设事情将同步执行。您正在发送请求,但不是等待每个请求完成后再发送下一个请求。另外,您的服务器代码假定碗是按碗的顺序到达的,这在internet上从来没有给出过。我建议将碗订单作为请求的一部分发送到/bowl,以防碗请求出现问题。哦,有趣的想法,我会试试。卢克,你是一名学者,也是一名ge