angularjs - chai-as-promised erroneously passes tests -


i'm trying write tests method returns angular promise ($q library).

i'm @ loss. i'm running tests using karma, , need figure out how confirm accountsearchresult.validate() function returns promise, confirm whether promise rejected or not, , inspect object returned promise.

for example, method being tested has following (simplified):

.factory('accountsearchresult', ['$q',   function($q) {    return {     validate: function(result) {       if (!result.accountfound) {         return $q.reject({           message: "that account or userid not found"         });       }       else {         return $q.when(result);       }     }   }; }]); 

i thought write test this:

    it("it should return object message property", function () {       promise = accountsearchresult.validate({accountfound:false});       expect(promise).to.eventually.have.property("message"); // passes     }); 

that passes, (erroneously):

    it("it should return object message property", function () {       promise = accountsearchresult.validate({accountfound:false});       expect(promise).to.eventually.have.property("i_dont_exist"); // passes, should fail     }); 

i trying use chai-as-promised 'eventually', tests pass false positives:

    it("it should return object", function () {       promise = accountsearchresult.validate();       expect(promise).to.eventually.be.an('astronaut');     }); 

will pass. in looking @ docs , questions, have seen examples such as:

expect(promise).to.eventually.to.equal('something'); return promise.should.eventually.equal('something'); expect(promise).to.eventually.to.equal('something', "some message expectation."); expect(promise).to.eventually.to.equal('something').notify(done); return assert.becomes(promise, "something", "message assertion"); wrapping expectation in runs() block wrapping expectation in settimeout() 

using .should gives me cannot read property 'eventually' of undefined. missing?

@runtarm 's suggestions both spot on, turns out. believe root of issue angular's $q library tied angular's $digest cycle. while calling $apply works, believe reason works because $apply ends calling $digest anyway. typically i've thought of $apply() way let angular know happening outside world, , didn't occur me in context of testing, resolving $q promise's .then()/.catch() might need pushed along before running expectation, since $q baked angular directly. alas.

i able working in 3 different ways, 1 runs() blocks (and $digest/$apply), , 2 without runs() blocks (and $digest/$apply).

providing entire test overkill, in looking answer found myself wishing people had posted how injected / stubbed / setup services, , different expect syntaxes, i'll post entire test.

describe("appaccountsearchservice", function () {   var expect = chai.expect;    var $q,     authorization,     accountsearchresult,     result,     promise,     authobj,     reasonobj,     $rootscope,     message;    beforeeach(module(     'authorization.services',     // dependency service need stub out     'app.account.search.services' // 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   beforeeach(inject(function($injector) {     authobj.empaccess = false; // mocking out specific value on object   }));    // set spies/stubs   beforeeach(function () {     sinon.stub(authorization, "isemployeeaccount").returns(true);   });    describe("accountsearchresult", function () {      describe("validate", function () {        describe("when service says account 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... 'magic' needed           $rootscope.$apply();         });          it("should return object", function () {           expect(reasonobj).to.be.an.object;         });          it("should have entered 'catch' function", function () {             expect(message).to.equal("promise rejected");         });          it("should return object message property", function () {           expect(reasonobj).to.have.property("message");         });         // other tests...       });         describe("when account id falsey", function() {         // example of using runs() blocks.         //note first runs() content done in beforeeach(), above         it("should not have entered 'then' function", function () {             // executes in block first.           // $rootscope.apply() pushes promise resolution .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();           });            // reasonobj has been populated in prior runs() bock, can test in runs() block.           runs(function() {             expect(reasonobj).to.not.equal("promise resolved");           });          });         // more tests.....       });        describe("when account employee account", function() {         describe("and user 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 works             $rootscope.$digest();           });            it("should return object", function () {             expect(reasonobj).to.be.an.object;           });            // more tests ...         });       });     });   }); }); 

now know fix, obvious reading $q docs under testing section, says call $rootscope.apply(). since able working both $apply() , $digest(), suspect $digest needs called, in keeping docs, $apply() 'best practice'.

decent breakdown on $apply vs $digest.

finally, mystery remaining me why tests passing default. know getting expectations (they being run). why expect(promise).to.eventually.be.an('astronaut'); succeed? /shrug

hope helps. push in right direction.


Comments

Popular posts from this blog

java - How to specify maven bin in eclipse maven plugin? -

single sign on - Logging into Plone site with credentials passed through HTTP -

php - Why does AJAX not process login form? -