How to test a method that uses async/await?
Asked Answered
C

2

5

I've seen a lot of articles about how use async/await in your unit tests, but my need is the opposite.

How do you write a test for a method that uses async/await?

My spec is not able to reach any code after the 'await' line. Specifically, the spec fails in two ways.

1) HelloWorld.otherCall returns undefined instead of the return value I specify

2) HelloWorld.processResp never gets called

class HelloWorld {

    async doSomething(reqObj) {
        try {
           const val = await this.otherCall(reqObj);
           console.warn(val); // undefined
           return this.processResp(val);
        }
    }

}

describe('HelloWorld test', function () {

    let sut = new HelloWorld(); //gross simplification for demo purposes

    describe('doSomething()', function () {
        beforeEach(function mockInputs() {
           this.resp = 'plz help - S.O.S.';
        });

        beforeEach(function createSpy() {
            spyOn(sut, 'otherCall').and.returnValue( $q.resolve(this.resp) );
            spyOn(sut, 'processResp');
        });

        it('should call otherCall() with proper arguments', function () {
            //this test passes   
        });

        it('should call processResp() with proper arguments', function () {
           sut.doSomething({});
           $rootScope.$apply(); //you need this to execute a promise chain..

           expect(sut.processResp).toHaveBeenCalledWith(this.resp); 
           //Expected spy processResp to have been called with [ 'plz help SOS' ] but it was never called.
        });
    });
});

Running angular 1.5 and jasmine-core 2.6.

Cheddar answered 19/1, 2018 at 17:45 Comment(0)
A
9

The .then of a promise is overloaded to handle either promises or values, and await is syntactic sugar for calling then.

So there is no reason your spy would be required to return a promise, or even a value. Returning at all, even if undefined, should trigger the await to fire, and kick off the rest of your async function.

I believe your problem is that you are not waiting for the doSomething promise to resolve before trying to test what it did. Something like this should get you more in the ballpark.

it('should call processResp() with proper arguments', async function () {
   await sut.doSomething({});
   // ...
});
Abdicate answered 1/2, 2018 at 0:41 Comment(0)
D
0

Jasmine has Asynchronous Support. You can probably find a solution that way.

Personally, I think you should not test such methods at all.

Testing state means we're verifying that the code under test returns the right results.

Testing interactions means we're verifying that the code under test calls certain methods properly.

At most cases, testing state is better.

At your example,

async doSomething(reqObj) {
    try {
       const val = await this.otherCall(reqObj);
       return this.processResp(val);
    }
}

As long as otherCall & processResp are well covered by unit tests your good.

Do something should be covered by e2e tests.

you can read more about it at http://spectory.com/blog/Test%20Doubles%20For%20Dummies

Doublure answered 22/1, 2018 at 7:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.