使用Jasmine进行Angular2异步单元测试

7

我在编写一个Angular2应用程序,并且在使用Jasmine编写异步代码测试方面遇到了困难。出于某种原因,我没有看到很多似乎非常适用于我的情况的例子。

我目前正在尝试测试一个具有对另一个服务的异步依赖项的服务(而不是组件)。 我不确定在测试的哪个阶段检查异步调用的结果是有效的。是否可以在服务的异步处理程序内部调用expect()

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

这是完整的测试。

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();
        });
}));

通常情况下,您只需将 expect 放在 then 中。在异步测试中,您需要使用 asyncfakeAsync 包装 inject - Estus Flask
1个回答

22

如果您想确保测试的可靠性,处理异步测试有两个方面是需要关注的。

首先,必须确保在尝试进行测试之前等待异步结果可用。

因此,如果异步结果是一个promise,您可以将expect置于then处理程序中,正如您在问题中指出的那样。

您需要考虑的第二个问题是,在执行期望之前,强制测试本身等待。如果您不处理这个问题,可能会出现期望失败的情况,但由于您的测试在完成之前没有等待异步操作完成,测试会报告一个错误的结果。

有几种方法可以“使您的测试等待”。

纯jasmine的方法是将一个done处理器传递到您的it函数中。然后jasmine将等待该done处理器被调用后再认为测试已经完成。

例如:

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

然而,Angular的测试框架对此添加了另外两个选项。第一个选项如果您对异步编程感到熟悉,那么更容易理解。

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

在这种情况下,你基本上需要将测试处理程序包装在一个异步函数中。该函数会强制测试等待任何异步结果(例如承诺、观察等)返回结果,然后允许测试完成。

第二种方法是使用fakeAsync,它允许你完全隐藏测试的异步特性。

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挂钩到所有异步函数,并允许您将它们视为同步函数。您可以使用tick()函数在继续之前“强制测试等待”异步任务完成。
(它不完全是这样的,但在概念上,您可以这样考虑)。

请参阅Angular文档以了解更多信息


好的回答。一个问题:在使用async()的示例中,您仍然调用了jasmine的done()函数。这是必要的吗? - d512
@d512,你说得对 - 如果使用async,done()调用是不必要的。我已经更新了答案中的代码以反映这一点。 - snorkpete

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接