我正在尝试为返回角度承诺($ q库)的方法编写测试。
我很茫然。我正在使用Karma运行测试,我需要弄清楚如何确认AccountSearchResult.validate()
函数返回一个promise,确认promise是否被拒绝,并检查使用promise返回的对象。
例如,正在测试的方法具有以下(简化):
.factory('AccountSearchResult', ['$q',
function($q) {
return {
validate: function(result) {
if (!result.accountFound) {
return $q.reject({
message: "That account or userID was not found"
});
}
else {
return $q.when(result);
}
}
};
}]);
我以为我可以写一个这样的测试:
it("it should return an object with a message property", function () {
promise = AccountSearchResult.validate({accountFound:false});
expect(promise).to.eventually.have.property("message"); // PASSES
});
过去了,但这也是错误的:
it("it should return an object with a message property", function () {
promise = AccountSearchResult.validate({accountFound:false});
expect(promise).to.eventually.have.property("I_DONT_EXIST"); // PASSES, should fail
});
我正在尝试使用chai-as-promised'最终',但我的所有测试都通过了误报:
it("it should return an object", function () {
promise = AccountSearchResult.validate();
expect(promise).to.eventually.be.an('astronaut');
});
将通过。在查看文档和SO问题时,我看到了以下示例:
expect(promise).to.eventually.to.equal('something');
return promise.should.eventually.equal('something');
expect(promise).to.eventually.to.equal('something', "some message about expectation.");
expect(promise).to.eventually.to.equal('something').notify(done);
return assert.becomes(promise, "something", "message about assertion");
wrapping expectation in runs() block
wrapping expectation in setTimeout()
使用.should
给我Cannot read property 'eventually' of undefined
。我错过了什么?
答案 0 :(得分:9)
.then()
/可能需要在运行期望之前推动.catch()
,因为$ q会直接变为角度。唉。
我能够以3种不同的方式使用它,一个用runs()
块(和$ digest / $ apply),两个没有runs()
块(和$ digest / $ apply)。
提供整个测试可能有点过分,但在寻找答案时我发现自己希望人们发布他们注入/存根/设置服务的方式以及不同的expect
语法,所以我会发布我的整个测试。
describe("AppAccountSearchService", function () {
var expect = chai.expect;
var $q,
authorization,
AccountSearchResult,
result,
promise,
authObj,
reasonObj,
$rootScope,
message;
beforeEach(module(
'authorization.services', // a dependency service I need to stub out
'app.account.search.services' // the service module I'm testing
));
beforeEach(inject(function (_$q_, _$rootScope_) {
$q = _$q_; // native angular service
$rootScope = _$rootScope_; // native angular service
}));
beforeEach(inject(function ($injector) {
// found in authorization.services
authObj = $injector.get('authObj');
authorization = $injector.get('authorization');
// found in app.account.search.services
AccountSearchResult = $injector.get('AccountSearchResult');
}));
// authObj set up
beforeEach(inject(function($injector) {
authObj.empAccess = false; // mocking out a specific value on this object
}));
// set up spies/stubs
beforeEach(function () {
sinon.stub(authorization, "isEmployeeAccount").returns(true);
});
describe("AccountSearchResult", function () {
describe("validate", function () {
describe("when the service says the account was not found", function() {
beforeEach(function () {
result = {
accountFound: false,
accountId: null
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
message = "PROMISE REJECTED";
reasonObj = arg;
});
// USING APPLY... this was the 'magic' I needed
$rootScope.$apply();
});
it("should return an object", function () {
expect(reasonObj).to.be.an.object;
});
it("should have entered the 'catch' function", function () {
expect(message).to.equal("PROMISE REJECTED");
});
it("should return an object with a message property", function () {
expect(reasonObj).to.have.property("message");
});
// other tests...
});
describe("when the account ID was falsey", function() {
// example of using runs() blocks.
//Note that the first runs() content could be done in a beforeEach(), like above
it("should not have entered the 'then' function", function () {
// executes everything in this block first.
// $rootScope.apply() pushes promise resolution to the .then/.catch functions
runs(function() {
result = {
accountFound: true,
accountId: null
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
reasonObj = arg;
message = "PROMISE REJECTED";
});
$rootScope.$apply();
});
// now that reasonObj has been populated in prior runs() bock, we can test it in this runs() block.
runs(function() {
expect(reasonObj).to.not.equal("PROMISE RESOLVED");
});
});
// more tests.....
});
describe("when the account is an employee account", function() {
describe("and the user does not have EmployeeAccess", function() {
beforeEach(function () {
result = {
accountFound: true,
accountId: "160515151"
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
message = "PROMISE REJECTED";
reasonObj = arg;
});
// digest also works
$rootScope.$digest();
});
it("should return an object", function () {
expect(reasonObj).to.be.an.object;
});
// more tests ...
});
});
});
});
});
现在我知道修复了,通过阅读测试部分下的$q docs显而易见,它明确地说要调用$ rootScope.apply()。由于我能够使用$ apply()和$ digest(),我怀疑$ digest真的需要调用,但是为了与文档保持一致,$ apply()可能是'最佳实践'
$apply vs $digest的体面细分。
最后,对我来说唯一的谜是为什么测试默认通过。我知道我达到了期望(他们正在运行)。那么为什么expect(promise).to.eventually.be.an('astronaut');
会成功呢? /耸肩
希望有所帮助。感谢你推动正确的方向。