使用Sinon.js测试Promises / Async流

时间:2014-04-02 17:57:38

标签: javascript unit-testing promise mocha sinon

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

Javascript方法(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回调中的步骤,然后步入到断言的测试承诺回调),但是测试中的name变量仍然会到来回到未定义。任何反馈都将不胜感激。

1 个答案:

答案 0 :(得分:0)

您正试图在data.name中同步返回serviceClient.getProductName,因为它依赖于异步的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);
    });
  });
});

抛开代码,概念上的错误是你可以从name同步返回getProductName,当该函数异常获取产品时(它无法访问其名称,直到延期已解决)。


注意:您可以使用getProductName来实现then,它返回一个Promise(它是Deferred的子集,但您通常可以使用它并且代码看起来很像甚至更清楚):

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

要删除它,也是不必要的$.when(...),您也可以从_getProduct返回一个Promise(如果您已经延期,获得Promise就像调用{一样简单} {1}})。