sinon spy 没有在生成器循环中注册调用?

sinon spy doesn't register call in a generator loop?

我想检查是否正在调用一段代码,所以我使用 sinon 间谍来断言它。然而,尽管 console.logs 显示代码已被正确调用,但间谍似乎失败了。

我想知道我的函数作为生成器是否导致我的间谍误报它在做什么。

我的代码(为简洁起见,我删除了一些代码块):

  isBlacklisted(release, jobUUID) {

    names.forEach((name) => {
      this._spawnPythonProcessGenerator(
        this.IS_BLACKLISTED_SCRIPT,
        name
      ).next().value
        .then((data) => {
          console.log(data);
        })
        .catch((err) => {
          this._errorEvent(release, name, err, jobUUID);
        });
    }, this);
  }

  _errorEvent(release, name, err, jobUUID) {
    console.log('got here');
  }

  *_spawnPythonProcessGenerator(scriptSrc, name) {
    const pythonProcess = this._childProcess.spawn(
      'python3',
      [...arguments]
    );
    yield new Promise((resolve, reject) => {
      pythonProcess.stderr.on('data', (err) => {
        reject(err.toString());
      });

      pythonProcess.stdout.on('data', (data) => {
        resolve(data.toString());
      });
    });
  }

和我的测试:

const Blacklist = require('../../src/Blacklist2');
const childProcess = require('child_process');
const uuid = require('uuid/v4');

describe('Blacklist', () => {
  let blacklist;
  beforeEach(() => {
    blacklist = new Blacklist(childProcess);
    blacklist.IS_BLACKLISTED_SCRIPT = './test/helpers/good.py';
  });
  describe('isBlacklisted', () => {
    it('should call the _errorEvent for every name in a release when the blacklist application is not available', async () => {
      let release = {
        id: 1001,
        asset_controller: {
          id: 54321,
        },
        display_name: 'Blah',
        names: [
          {
            id: 2001,
            name: 'Blah',
          },
        ],
      };

      blacklist.IS_BLACKLISTED_SCRIPT = './test/helpers/'+ uuid() +'.py';


      const spy = sinon.spy(blacklist, '_errorEvent');
      blacklist.isBlacklisted(release, uuid());

      console.log(spy);
      sinon.assert.calledTwice(spy);
      spy.restore();
    });
  });
});

我的间谍报告:

notCalled: true

我会将我的评论扩展为实际答案,希望对您有所帮助。

你的问题出在异步,而不是生成器。你需要 isBlacklisted 到 return 一个你可以等待的承诺。否则你的断言发生在间谍被调用之前。

像这样:

isBlacklisted(release, jobUUID) {
    let promises = names.map((name) => {
      return this._spawnPythonProcessGenerator(
        this.IS_BLACKLISTED_SCRIPT,
        name
      ).next().value
        .then((data) => {
          console.log(data);
        })
        .catch((err) => {
          this._errorEvent(release, name, err, jobUUID);
        });
    }, this);

    return Promise.all(promises);
}

然后,在你的测试中:

return blacklist.isBlacklisted(release, uuid())
    .then(() => {
        sinon.assert.calledTwice(spy);
    });

另外...这与您的问题无关,但您的 _spawnPythonProcessGenerator 方法不需要是生成器。您只是通过像那样调用 next 并为每个数组项重新调用整个过程来使用它的第一个值。

*去掉,把yield改成return,调用时跳过.next().value也是一样的。您可能还想重命名它,因为它不是生成器。

_spawnPythonProcess(scriptSrc, name) {
    const pythonProcess = this._childProcess.spawn(
      'python3',
      [...arguments]
    );
    return new Promise((resolve, reject) => {
      pythonProcess.stderr.on('data', (err) => {
        reject(err.toString());
      });

      pythonProcess.stdout.on('data', (data) => {
        resolve(data.toString());
      });
    });
}

当你调用它时:

let promises = names.map((name) => {
  return this._spawnPythonProcess(
    this.IS_BLACKLISTED_SCRIPT,
    name
  )
    .then((data) => {
      console.log(data);
    })
    .catch((err) => {
      this._errorEvent(release, name, err, jobUUID);
    });
}, this);

return Promise.all(promises);