Mocha 无法找到您的测试,因为您没有正确构建它们。 it() 块应该立即在describe() 块内(并且describe() 块可以在您认为合适的情况下相互嵌套)。如果你想使用 Promise,Promise 应该放在 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');
});
});
});
});
请注意,承诺现在包含在 it() 的正文中。 Mocha 现在应该能够找到您的测试。然而,它不会通过。为什么不呢?
您可能知道,promise 是异步的。使用 mocha 测试异步代码需要使用 done 回调 - 有关更多信息,请参阅 guide。基本上,这意味着it() 块应该接受一个名为done 的参数(名称很重要!)。这个done thingy 是 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();
});
});
});
});
注意,我只是将done参数添加到it(),然后在then()的底部调用。
此时,代码应该可以工作,我认为...我不确定,因为我无法对其进行测试。不过,我们还可以做更多的事情来进一步改进这一点。
首先,我质疑你在这里使用承诺。如果您有一个用于获取访问令牌的 API,那么我会选择让该 API 返回一个 Promise,因为 Promise 对调用者来说非常方便。但是,我相信您已经注意到,构建 Promise 可能有点乏味,而且我认为它不会为您的代码增加太多价值。我会选择这样做:
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();
});
});
});
});
这不是更短更甜吗?代码仍然是异步的,所以您仍然应该确保您的 it() 块接受 done 回调,并且您应该在测试完成后调用它。
现在,如果你仍然坚持使用 Promise,那么我还要警告你一件事。如果您的 .then() 处理程序代码中有错误会发生什么?嗯,照着documentation:
如果被调用的处理程序抛出异常,那么 Promise
.then 返回的结果被拒绝。
你的承诺有拒绝处理程序吗?不,那是什么意思?这意味着错误将被无声无息地吞噬。您的测试将因Error: timeout of 2000ms exceeded 而失败,这是因为从未调用过done 处理程序,但不会显示错误的实际原因,您将费尽心思试图找出问题所在.
那你能做什么?您可以使用 .then() 的第二个参数来指定拒绝处理程序,并且在那里,您可以利用这样一个事实,即 mocha 传递给您的测试的 done 回调接受错误参数,所以如果您调用 @ 987654354@,你的测试将失败(这就是我们在这种情况下想要的),“某事”将是原因。所以这就是你的情况:
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);
});
});
});
});
但是,我们可以做得更好。考虑如果从拒绝处理程序中抛出错误会发生什么。不太可能,因为你在那里没有做很多事情——只是打电话给done(err)。但如果真的发生了怎么办?嗯,几乎是一样的——错误会被默默地吞掉,测试会失败,出现非特定的timeout 错误,你会再次拔掉头发。有没有办法让这个错误冒泡并被重新抛出?
事实上,有:Q 和您正在使用的 promise 库都有一个名为 .done() 的备用处理程序(不要与 mocha 的 done 回调混淆)。它类似于.then(),但在未捕获异常方面的行为略有不同。来自documentation:
Promise#done(onFulfilled, onRejected)
与 .then 的语义相同,只是它不返回承诺
并且重新抛出任何异常以便记录它们(崩溃
非浏览器环境中的应用程序)
完美,这正是我们想要的。请务必了解什么时候应该使用.then(),什么时候应该使用.done()。 Q API 在解释方面做得非常出色(并且您使用的 promise 库具有类似的行为 - 我测试过):
done 与 then 用法的黄金法则是:要么返回你的承诺
给其他人,或者如果链以您结束,请致电done 终止
它。以catch 终止是不够的,因为catch 处理程序
本身可能会引发错误。
(注意:.catch() 似乎是特定于 Q 的,但它与 onRejected 回调非常相似,它是 .then() 的第二个参数。)。
因此,考虑到这一点,只需将您的最后一个 .then() 替换为 .done()。当您使用.done() 时,您可以省略拒绝处理程序并依靠承诺库重新抛出任何未处理的期望,因此您将获得错误描述和堆栈跟踪。考虑到上述情况,您的代码现在看起来像:
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();
});
});
});
});
基本上,忽略之前的代码示例,与之前的唯一区别是我们使用.done() 而不是.then()。
希望这涵盖了您入门所需的大部分内容。您可能还需要考虑其他一些事情,例如可能在 before() 挂钩而不是 it() 块中检索身份验证密钥(因为我假设您正在测试的真实内容不是检索密钥- 这只是测试你真正想要测试的东西的先决条件,所以钩子可能更合适 - see here)。我还质疑您是否应该从测试中连接到外部系统,而不是仅仅将其排除(这取决于这些是否是unit tests or integration tests)。而且我相信您可以提出一个更好的断言,即仅检查 token 是一个字符串,例如使用正则表达式来确保它与模式匹配,或者实际测试对受保护资源的请求并确保它穿过去。但我会把这些问题留给你思考。