简体   繁体   中英

Angular2 async unit testing with jasmine

I'm writing an angular2 app and having trouble understanding how to write tests for async code using jasmine. For whatever reason, I'm not seeing a lot of examples that seem terribly applicable to my situation.

I'm currently trying to test a service (not a component) that has a async dependency on another service. I'm not 100% sure at what point in the test it's valid to check the results of the async call. Can you just call expect() inside the async handler for your service?

service.foo()
    .then((data) => {
        //do I check the results in here?
        expect(data).toEqual({ a: 1, b: 2 });
        expect(mockDep.get).toHaveBeenCalled();
    });

Here is the full test.

import { TestBed, inject } from '@angular/core/testing';
import { MyService } from './my.service.ts';
import { MyDependency } from './dependency.service.ts';

class MockDependency {
    doSomething(): Promise<any> {
        throw Error('not implemented');
    };
}

describe('some tests', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                MyService,
                {
                    provide: MyDependency, useClass: MockDependency
                }
            ]
        });
    });
});

it('should do something', inject([MyService, MyDependency], (service: MyService, mockDep: MyDependency) => {
    spyOn(mockDep, 'doSomething').and.callFake(function () {
        return Promise.resolve({ a: 1, b: 2 });
    });

    service.foo()
        .then((data) => {
            //do I check the results in here?
            expect(data).toEqual({ a: 1, b: 2 });
            expect(mockDep.get).toHaveBeenCalled();
        });
}));

There's two aspects of dealing with async tests that you have to concern yourself about if you want to ensure your tests are actually reliable.

First, you have to ensure that if a result is retrieved asynchronously, that you wait until the result is available before you try to test for it.

Hence, if the asynchronous result is a promise, for eg, you can put your expect in your then handler, just as you indicated in your question.

The second issue that you have to concern yourself with is forcing your test itself to wait for your expectations to execute before giving a positive (or negative) result. If you don't deal with this, you can have cases where your expectation fails, but because your test did not wait for your asynchronous action to complete before finishing, the test reports a false positive.

There are several ways of 'making your test wait'.

The pure jasmine way is to pass a done handler into your it function. Then jasmine will wait until that done handler is called before considering the test complete.

eg.

it('tests an async action', (done) => {
   asyncAction().then(result => {
     expect(result).toEqual(true);
     done();
   });
});

However, angular's testing framework adds two other options to this. The first is easier to grasp if you are comfortable with async programming.

it('tests an async action', async(() => {
   asyncAction().then(result => {
     expect(result).toEqual(true);
   });
}));

in this case, you basically wrap your test handler in an async function. This function will force the test to wait for any async results (eg promises, observables etc) to return a result, before allowing the test to complete.

The second method is to use fakeAsync, which allows you to hide the async nature of your test entirely.

it('tests an async action', fakeAsync(() => {
   let myResult;
   asyncAction().then(result => {
     myResult = result;
   });

   tick();   <--- force all async actions to complete
   expect(myResult).toEqual(true);
}));

fakeAsync hooks into all async functions and allows you to treat them as synchronous. You can use the tick() function to 'force your test to wait' for async tasks to complete before continuing.
(It's not quite doing that, but conceptually, you can think of it that way).

See the Angular docs to learn more

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM