You're trying to return data.name
synchronously in serviceClient.getProductName
, which can't be done since it depends on an asynchronous ajax request.
You're doing return data.name;
inside your callback, which won't get you the expected result: if you'd return something synchronously, that return
sentence should be at the scope outside that closure.
To simplify: if there's anything you can return there, it's a Deferred or Promise. It should be something like this:
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;
};
Then, in your test:
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);
});
});
});
Leaving code aside, the conceptual mistake is that you can return name
synchronously from getProductName
, when that function gets the product asyncrhonously (it won't be able to access its name until the deferred is resolved).
Note: you could implement getProductName
using then
, that returns a Promise (which is a subset of Deferred, but you can usually get away with it and code looks even clearer):
serviceClient.getProductName = function(id, storeId) {
return $.when(self._getProduct(id)).then(function(data) {
return data.name;
});
};
To remove that, also unnecessary $.when(...)
, you could return a Promise from _getProduct
as well (if you've got a deferred, getting a Promise for it is as simple as calling deferred.promise()
).