2

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

在尝试使罢工测试用例工作的同时编写罢工测试用例后,当我尝试在命令行使用mocha运行以下测试时,我收到以下超测错误:"Uncaught TypeError: undefined is not a function" at /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)

游戏.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);

测试/test.js

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);         
        });
    });         
});
4

1 回答 1

6

添加到我之前的评论

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

你应该有一个回调: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-as-promised而不是 supertest,因为当您需要运行大量请求时,这可以使事情变得更容易。这个库,加上bluebird可能会使像你这样的测试更简单。

这个要点有我重写你的测试,使用Bluebirdpromises 和supertest-as-promised,并评论我所做的改变。

于 2014-08-05T23:36:38.053 回答