yammerade
yammerade

Reputation: 629

Unit testing logic inside promise callback

I have an ES6 / Aurelia app that I am using jasmine to test. The method I am trying to test looks something like this:

update() {
    let vm = this;
    vm.getData()
        .then((response) => {
            vm.processData(response);
        });
}

Where this.getData is a function that returns a promise.

My spec file looks something like this:

describe('my service update function', () => {
    it('it will call the other functions', () => { 
        myService = new MyService();
        spyOn(myService, 'getData').and.callFake(function() {
            return new Promise((resolve) => { resolve(); });
        });
        spyOn(myService, 'processData').and.callFake(function() { return; });
        myService.update();

        // this one passes
        expect(myService.getData).toHaveBeenCalled();

        // this one fails
        expect(myService.processData).toHaveBeenCalled();
    });
});

I understand why this fails - promises are asynchronous and it hasn't been resolved by the time it hits the expect.

How can I push the promises to resolve from my test so that I can test the code inside the call back?

jsfiddle of failed test: http://jsfiddle.net/yammerade/2aap5u37/6/

Upvotes: 2

Views: 1864

Answers (3)

urvianoob
urvianoob

Reputation: 302

Adding on to @yammerade's answer

We can make a fake object that behaves like a promise instead of returning an actual promise. The following code also allows chaining of .then(...).catch(...)

class MockPromise {
    constructor(data, err) {
        this.data = data;
        this.err = err;
    }

    then(callback) {
        callback(this.data);
        return this;
    }

    catch(callback) {
        callback(this.err);
        return this;
    }
};

var promise = new MockPromise('resolved data here', 'resolved error here');

// resolve your promises like normal
promise.then(function(data) {
    ...
}).catch(function(err) {
    ...
});

This helps a lot when you want to test callback logic within a Jasmine Spy Obj

Upvotes: 0

Lesha Ogonkov
Lesha Ogonkov

Reputation: 1238

it((done) => {
  // call done, when you are done
  spyOn(myService, 'processData').and.callFake(function() {
    expect(myService.processData).toHaveBeenCalled();

    done();
  });
})

Upvotes: 0

yammerade
yammerade

Reputation: 629

I got a workaround running by returning an object that behaves like a promise instead of an actual promise

describe('my service update function', () => {
    it('it will call the other functions', () => { 
        myService = new MyService();
        spyOn(myService, 'getData').and.returnValue({
            then(callback) {
                callback();
            }
        });
        spyOn(myService, 'processData').and.callFake(function() { return; });
        myService.update();

        // this one passes
        expect(myService.getData).toHaveBeenCalled();

        // this one fails
        expect(myService.processData).toHaveBeenCalled();
    });
});

Fiddle here: http://jsfiddle.net/yammerade/9rLrzszm/2/

Is there anything wrong with doing it this way?

Upvotes: 7

Related Questions