使用 Sinon.js 测试承诺/异步流

Testing Promises/Async flow with Sinon.js

本文关键字:异步 承诺 测试 Sinon js 使用      更新时间:2023-09-26

我在测试一个函数的返回值时遇到问题,该函数在执行之前等待承诺被解析。

Javascript Method (serviceClient._getProduct 返回 jQuery ajax promise 对象)

serviceClient.getProductName = function(id, storeId) {
  $.when(self._getProduct(id)).done(function(data) {
    return data.name;
  });
};

测试代码

before(function() {
  serviceClient.sampleResponse = fixture.load('product_response.json')[0];
  $.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
  it('should return the product name', function() {
    var name = serviceClient.getProductName(serviceClient.sampleResponse.id);
    $.when($.ajaxStub.returnValue).done(function() {
      expect(name).to.equal(serviceClient.sampleResponse.name);
    });
  });
});

当我单步执行调用堆栈时,它似乎正在正确执行(在实际 js 文件中的 promise 回调内执行步骤,然后步入测试承诺回调以断言),但是测试中的名称变量仍然以未定义的形式返回。任何反馈将不胜感激。

您正在尝试在 serviceClient.getProductName 中同步返回data.name,这无法完成,因为它依赖于异步 ajax 请求。

你在回调中执行return data.name;操作,这不会得到预期的结果:如果你同步返回一些东西,那么return句子应该在该闭包之外的范围内。

简单来说:如果有什么东西可以退回,那就是延期或承诺。它应该是这样的:

serviceClient.getProductName = function(id, storeId) {
  var deferredName = $.Deferred();
  $.when(self._getProduct(id)).done(function(data) {
    deferredName.resolve(data.name);
  }).fail(function(jqXHR, textStatus, error) {
    deferredName.reject(error);
  });
  return deferredName.promise();
  // Or, if needed (I don't think so, it's resolved and rejected here)
  // return deferredName;
};

然后,在您的测试中:

before(function() {
  serviceClient.sampleResponse = fixture.load('product_response.json')[0];
  $.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
  it('should return the product name', function() {
    serviceClient.getProductName(serviceClient.sampleResponse.id)
      .done(function(name) {
        expect(name).to.equal(serviceClient.sampleResponse.name);
    });
  });
});

撇开代码不谈,概念上的错误是,当该函数异步获取产品时,您可以从getProductName同步返回name(在解决延迟之前,它无法访问其名称)。


注意:您可以使用 then 来实现getProductName,它返回一个 Promise(它是延迟的子集,但您通常可以侥幸逃脱,代码看起来更清晰):

serviceClient.getProductName = function(id, storeId) {
  return $.when(self._getProduct(id)).then(function(data) {
    return data.name;
  });
};

要删除它,也是不必要的$.when(...),您也可以从_getProduct返回一个承诺(如果您有延迟,获得承诺就像调用deferred.promise()一样简单)。