使用 Promise 并跳过代码时,Mocha 测试超时,为啥?

Posted

技术标签:

【中文标题】使用 Promise 并跳过代码时,Mocha 测试超时,为啥?【英文标题】:Mocha test times out when using promises and skips over code, why?使用 Promise 并跳过代码时,Mocha 测试超时,为什么? 【发布时间】:2015-12-28 19:57:36 【问题描述】:

我已经尝试运行此测试 2 天了,但我无法弄清楚它有什么问题:

/*eslint-env mocha */
// var expect = require('chai').expect;
var chai = require('chai');
var chaiAsPromised = require("chai-as-promised");
var expect = chai.expect;
var Promise = require('bluebird');
var Archive = require('../lib/archive');
var path = require('path');
var fs = Promise.promisifyAll(require('fs-extra'));
var globAsync = Promise.promisify(require('glob'));
var tar = require('tar-fs');
var zlib = Promise.promisifyAll(require('zlib'));

chai.use(chaiAsPromised);

describe('Archive', function() 
  var pkg;
  var archive_location;
  var subject;

  beforeEach(function() 
    pkg = 
      name: 'test_0790feebb1',
      recipient_name: 'Test',
      files: 
        letter: '../documents/letters/test/letter.tex',
        resume: '../documents/cover/cover.tex'
      ,
      compiled_files: 
        package: '../documents/letters/test/test.pdf'
      
    ;
    archive_location = path.resolve('archives/test_0790feebb1.tar.gz'); 

    subject = new Archive(pkg);
  );

  after(function() 
    return globAsync('archives/test*')
      .each(function(filename) 
        return fs.removeAsync(filename);
      );
  );

  describe('#make', function() 
    it('has the correct directory structure', function() 
      // debugger;
      var tmp_extract_path = path.resolve('test/.tmp');
      var tarPromise = function(data) 
        console.log('tarP'); // never run
        return new Promise(function(reject, resolve) 
          data
            .pipe(zlib.Unzip())
            .pipe(tar.extract(tmp_extract_path))
            .on('error', reject)
            .on('end', resolve);
        );
      ;

      var verifyDir = function() 
        console.log('verD'); // never run
        return Promise.all([
            'code',
            'pdf',
            'code/repo',
            'code/documents',
            'code/documents/letters',
            'code/documents/letters/test',
            'code/documents/letters/shared',
            'code/documents/cover',
            'code/documents/letters'
        ].map(function(subpath) 
          return fs.statAsync(path.resolve(tmp_extract_path, subpath));
        ));
      ;

      return fs.createReadStreamAsync(archive_location)
        .then(function(data)  return tarPromise(data); )
        .then(function()  return verifyDir(); )
        .then(function(files) 
          console.log(files); // never run
          return expect(true).to.be.true;
        )
        .catch(function(e)  console.log(e); );
    );
  );
);

各种 console.log 甚至从未被执行,最终测试超时,没有任何错误或堆栈跟踪。

我不知道我做错了什么,承诺现在伤害了我的大脑。当我使用节点检查器运行代码并取消注释断点时,我可以看到this._runnable._trace 的值是"done() called multiple times"。我不知道这是否是一个实际错误,也不知道为什么如果这是一个错误它不会引发异常。我也无法解释为什么会发生这种情况,因为我不再使用任何带有承诺的 done() 回调,而且我的测试以 function() 而不是 function(done) 开始,就像异步测试一样

有什么想法吗?

【问题讨论】:

我看到的两个问题是:函数tarPromise返回未定义,不是promise,并且verifyDir没有在链中调用,只是传递。此外,在 Promise 链中添加一个 catch 块可能有助于识别问题...... 我的错,我在为 SO 格式化代码时打错了字。我编辑了问题以纠正错误。我还添加了一个 catch 块,但是当我运行代码时它从不打印任何内容。 大概是this issue? @mido22 是的,就是这个问题。我没有意识到这一点。谢谢 【参考方案1】:

您的问题是 fs.createReadStream 是同步的,并返回一个 ReadableStream,它将数据以块的形式读入内存。 PromisifyAll 将异步函数的回调转换为 Promise,而不是将同步函数转换为异步。

可能你想要的是使用fs.readFileAsync

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path')
var archive_location = path.resolve('example.zip');
var assert = require('assert');

describe('This', function()
    it('should work', function()
       return fs.readFileAsync(archive_location).
           then(function(data) 
                assert.notEqual(data.length, data.length);
                return data;
           ).
           catch(console.log);
    );
);

我将断言设置为失败的断言,以证明这实际上命中了承诺链。

【讨论】:

谢谢,根据您的回答,我最终在 tarPromise 中移动了 fs.createReadStream 并解决了问题。我希望 createReadStreamAsync 将流传递到下一个 then() 但事实并非如此。如果使用缓冲区,您的解决方案有效,我想坚持使用流,因为我的 tar.extract 与流一起使用。 如果你再次遇到这个问题,你可以用一个流包装一个缓冲区。请参阅***.com/a/16044400/151445 这并不能避免将整个文件读入内存缓冲区的问题,但它确实装饰了要由 tar.extract 使用的缓冲区。如果您的图书馆是公开的,那么将其公开给用户可能会更好。

以上是关于使用 Promise 并跳过代码时,Mocha 测试超时,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

在访问中提取一系列日期并跳过假期

当 app 是 Promise 时启动 mocha chai 测试

使用 Mocha 进行测试:基于 Promise 的测试不会自行运行?

Mocha.js 101同步异步与 Promise

返回 HTTP 错误 401 代码并跳过过滤器链

运行connectedAndroidTest并跳过卸载