leapin_leprechaun
leapin_leprechaun

Reputation: 605

Jasmine testing methods inside .done or .then

Wondering if anyone can help me - I'm trying to test my js using Jasmine (1.3) and I can't figure out the best way to test any method calls inside a .then or a .done method.

example code to explain:

Backbone.View.extend({

 myMethod: function () {
   this.something.done(function () {
    this.doSomethingElse();
   }.bind(this));
  }

 })

I'd like to write a test that check that this.doSomethingElse was called.

I was looking around at jasmine.async and a waitsFor/runs set up but I'm not sure how it fits into external code i.e. I'm not going to call done() inside my actual code to get my test working. Also if I mock out the done method on this.something then I'm not longer testing the actual implementation, right?

I'm just missing how things fit together. If anyone could point me in the right direction I'd really appreciate it!

Update: based on feedback below I've now tried the following

Hey, thanks for the answer - I think maybe I don't have the last part correct - have tried 2 different ways, both initial pass but then fail after a second or 2.

   it('calls doSomethingElse on done',function () {
     var mockDeferred = $.Deferred();

      myView.something = mockDeferred;
      spyOn(myView,'doSomethingElse');
      mockDeferred.resolve();

    waitsFor(function () {
      expect(myView.doSomethingElse).toHaveBeenCalled();
    });

  });

And also:

it('calls doSomethingElse on done',function () {
  var mockDeferred = $.Deferred(),
      someTrigger = false;

  myView.something = mockDeferred;
  spyOn(myView,'doSomethingElse');

  runs(function () {
    mockDeferred.resolve();
    someTrigger =  true;
  });

  waitsFor(function () {
    someTrigger =  true;
  });

  runs(function () {
    expect(myView.doSomethingElse).toHaveBeenCalled();
  });


});

In both instances the test will pass originally but then timeout to a failure after a second or 2.

Am I missing something?

Upvotes: 3

Views: 1074

Answers (2)

Boris Yakubchik
Boris Yakubchik

Reputation: 4453

Related problem

Spying on a method inside .then and expecting .toHaveBeenCalled fails

Solution:

run test inside fakeAsync and run tick() before the expect

Service:

getFirebaseDoc() {   
  this.db.firestore.doc('some-doc').get()
    .then(this.getFirebaseDocThen)
    .catch(this.getFirebaseDocCatch);
}

Unit testing:

it('should call getFirebaseDocThen', fakeAsync(() => {            // note `fakeAsync`
    spyOn(service, 'getFirebaseDocThen');
    spyOn(service.db.firestore, 'doc').and.returnValue({
      get: (): any => {
        return new Promise((resolve: any, reject: any): any => {
          return resolve({ exists: true });
        });
      },
    });
    service.getFirebaseDoc();
    tick();                                                       // note `tick()`
    expect(service.getFirebaseDocThen).toHaveBeenCalled();
}));

Upvotes: 0

Nathan Thompson
Nathan Thompson

Reputation: 2384

To test the example function you described, I would do the following within your test:

  • Create a new deferred object (I'll call it mockDeferred)
  • Pass mockDeferred into your code under test so that it is now this.something in your example
  • Spy on the doSomethingElse function
  • Call myMethod()
  • Call resolve() on mockDeferred
  • Assert that doSomethingElse was called

Edit based on OP's Update:

I don't see anywhere in either of your examples where you are calling myView.myMethod() within your test; make sure you do that. I whipped up an example that you can reference here.

As an aside, I'm surprised the second example you tried passes initially. Maybe because you have some code outside of a runs() block?

Upvotes: 2

Related Questions