摩卡:承诺中的断言不起作用

I11*_*in8 10 javascript mocha.js promise should.js sails.js

因此,我对mocha完全不熟悉,并且我被要求编写集成测试,将表单数据发布到页面,获取身份验证密钥,然后对密钥运行测试.

我正确地获得了密钥,这可以通过我的日志声明来确认,但是当我运行mocha时,我的测试表示0传递.它甚至没有打印出'describe'和'it'语句的描述,暗示它们在promise中不起作用.有没有我可以这样工作,同时仍然让断言访问我的授权密钥?

require('should');
require('sails');
var Promise = require("promise");
var request = require('request');

describe('Domain Model', function(){
  var options = { url:'link',
    form:{
      username: 'xxxxx@xxxxxx',
      password: '0000'
    },
    timeout: 2000
  };
  //request auth key
  var promise = new Promise(function(resolve,reject){
    request.post(options, function(error, response, body){
      //body contains the auth key, within a string of JSON
      resolve(body);
    });
  });
  //this is where I'm getting lost
  promise.then(function(data,test){
    var token = (JSON.parse(data))["access_token"];
    console.log(token);
      describe('Initialize',function(){
        it('should be an string', function(){
          (token).should.be.type('string');
        });

      });
  });

});
Run Code Online (Sandbox Code Playgroud)

Ser*_*y K 21

Mocha无法找到您的测试,因为您没有正确构建它们.该it()块应该立即在中describe()块(和describe()块可以相互嵌套,你认为合适).如果你想使用promises,承诺应该进入it()块内,而不是相反.所以,作为第一遍,更新后的代码看起来应该是这样的(我为了简洁而省略了一些部分):

describe('Domain Model', function(){
  var options = { ... };

  describe('Initialize',function(){
    it('should be an string', function(){
      //request auth key
      var promise = new Promise(function(resolve,reject){
        // ...
      });
      promise.then(function(data,test){
        var token = (JSON.parse(data))["access_token"];
        console.log(token);
        (token).should.be.type('string');
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

请注意,承诺现在包含在主体内部it().摩卡现在应该能够找到你的考试.但是,它不会通过.为什么不?

您可能知道,承诺是异步的.使用mocha测试异步代码需要使用done回调 - 有关详细信息,请参阅指南.基本上,这意味着it()块应该接受一个名为的参数done(名称很重要!).这个done东西是mocha自动传递的东西 - 它的存在向mocha表明这个测试包含异步代码,因此它直到你这么说才完成执行.表明测试完成的方式是执行done,因为done实际上是一个回调函数.您应该done()在您的测试被认为完成的任何时刻执行- 即,在任何代码块应该最后运行的末尾,在您的情况下,这是.then()承诺的处理程序的底部.因此,改进我们的上一个版本,代码现在看起来像这样(再次,为简洁起见,切割一些部分):

describe('Domain Model', function(){
  var options = { ... };

  describe('Initialize',function(){
    it('should be an string', function(done){
      //request auth key
      var promise = new Promise(function(resolve,reject){
        // ...
      });
      promise.then(function(data,test){
        var token = (JSON.parse(data))["access_token"];
        console.log(token);
        (token).should.be.type('string');
        done();
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

请注意,我刚刚添加了done参数it(),然后我在底部调用了它then().

在这一点上,代码应该工作,我想...我不确定,因为我无法测试它.但是,我们可以采取更多措施来进一步改进.

首先,我在这里质疑你对承诺的使用.如果您有一个用于获取访问令牌的API,那么我会选择让该API返回一个承诺,因为promises对调用者来说非常方便.但是,正如我相信您已经注意到,构建承诺可能有点单调乏味,我认为它不会为您的代码增加太多价值.我会选择这样做:

describe('Domain Model', function(){
  var options = { ... };

  describe('Initialize',function(){
    it('should be an string', function(done){
      //request auth key
      request.post(options, function(error, response, body){
        //body contains the auth key, within a string of JSON
        var token = (JSON.parse(body))["access_token"];
        console.log(token);
        (token).should.be.type('string');
        done();
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

这不是那么短,更甜吗?代码仍然是异步的,因此您仍应确保您的it()块接受done回调,并且您应该在测试完成后调用它.

现在,如果你仍然坚持使用承诺,那么我还有一件事要警告你.如果.then()处理程序代码中存在错误,会发生什么?好吧,根据文件:

如果被调用的处理程序抛出异常,那么.then返回的promise将被该异常拒绝.

你有承诺的拒绝处理程序吗?不,那是什么意思?这意味着错误将被无声地吞噬.你的测试将失败Error: timeout of 2000ms exceeded,这是因为done处理程序从未被调用过,但是错误的实际原因将不会显示出来,并且你会试着找出问题所在.

所以,你可以做什么?您可以使用第二个参数来.then()指定拒绝处理程序,在那里,您可以利用donemocha传入您的测试的回调接受错误参数的事实,因此如果您调用done("something"),您的测试将失败(是我们在这种情况下想要的东西),而"东西"将是原因.所以这就是你的情况:

describe('Domain Model', function(){
  var options = { ... };

  describe('Initialize',function(){
    it('should be an string', function(done){
      //request auth key
      var promise = new Promise(function(resolve,reject){
        // ...
      });
      promise.then(function(data){
        var token = (JSON.parse(data))["access_token"];
        console.log(token);
        (token).should.be.type('string');
        done();
      }, function (err) {
        done(err);
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

但是,我们可以做得更好.考虑如果从拒绝处理程序中抛出错误会发生什么.不太可能,因为你没有在那里做很多事 - 只是打电话done(err).但如果确实发生了怎么办?好吧,几乎是一样的 - 错误将被无声地吞噬,测试将失败并出现非特定timeout错误,你将再次拔出你的头发.有没有办法可以让这个错误冒出来并重新投入?

事实上,有:Q和你正在使用的promise库都有一个名为的替代处理程序.done()(不要与mocha的done回调混淆).它与.then()未捕获的异常类似,但行为略有不同.从文档:

承诺#done(onFulfilled,onRejected)

与.then相同的语义,除了它不返回promise并且重新抛出任何异常以便可以记录它们(在非浏览器环境中崩溃应用程序)

完美,这正是我们想要的.请务必了解何时应该使用.then(),以及何时应该使用.done().该Q API做了解释的了出色的工作(和你正在使用的承诺库有类似的行为-我测试):

donethen使用相关的黄金法则是:将您的承诺退还给其他人,或者如果链条以您结束,请致电done终止它.使用终止catch是不够的,因为catch处理程序本身可能会抛出错误.

(注意:.catch()似乎是特定于Q的,但它与onRejected回调非常相似,这是第二个参数.then().).

所以考虑到这一点,只需.then()用一个替换你的鞋楦.done().当您使用时.done(),您可以省略拒绝处理程序并依赖promise库重新抛出任何未处理的预期,因此您将获得错误描述和堆栈跟踪.考虑到上述情况,您的代码现在看起来像:

describe('Domain Model', function(){
  var options = { ... };

  describe('Initialize',function(){
    it('should be an string', function(done){
      //request auth key
      var promise = new Promise(function(resolve,reject){
        // ...
      });
      promise.done(function(data){
        var token = (JSON.parse(data))["access_token"];
        console.log(token);
        (token).should.be.type('string');
        done();
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

基本上,忽略前面的代码示例,与之前的代码示例的唯一区别是我们使用的.done()代替.then().

希望这涵盖了您开始所需的大部分内容.还有其他一些你可能想要考虑的事情,比如可能在before()钩子而不是it()块中检索auth键(因为我假设你正在测试的真实东西不是密钥的检索 - 这只是一个先决条件,测试你真正想测试的东西,所以钩子可能更合适 - 见这里).我还要问你是否应该从测试中连接到外部系统,而不仅仅是将其删除(这取决于它们是单元测试还是集成测试).我确信你可以提出一个更好的断言,只是检查这token是一个字符串,就像使用正则表达式来确保它匹配一个模式,或实际测试一个受保护资源的请求并确保它通过.但我会留下这些问题供你思考.

  • 你不再需要`function(done)`,在新版本的Mocha中,你可以简单地"返回"这个承诺,如果该promsie结算,它将通过测试. (9认同)