【问题标题】:Angular - How to unit test component with asynchronous service callAngular - 如何使用异步服务调用对组件进行单元测试
【发布时间】:2017-10-25 17:27:56
【问题描述】:

我有以下从 Angular 服务中检索数据的组件:

export class MyComponent {
    constructor() {
        myService.get().then(() => {
            console.log('hello from constructor');
        });
    }
}

然后是我的单元测试:

///////////

it('does something', () => {
    console.log('hello from unit test');
});

///////////

不幸的是,这会导致以下日志:

> hello from unit test
> hello from constructor

如何确保构造函数在运行单元测试之前完成?

【问题讨论】:

  • 您的问题实际上并不是在测试之前没有调用构造函数,而是关于您希望在测试之前进行服务调用。可能想要隔离它(即从图片中删除构造函数),这样你的问题就更清楚了。 (海事组织)
  • 不是hello from constructor。它是hello from asynchronous code that runs in constructor。一般来说,这样的事情是一种反模式。

标签: angular unit-testing


【解决方案1】:

不要使用构造函数加载数据,而是实现OnInit接口。

import { OnInit } from '@angular/core';
export class MyComponent implements OnInit {

    constructor(private myService: MyService) {}

    ngOnInit() {
        myService.get().then(() => {
            console.log('hello from constructor');
        });
    }
}
  • 另见角度文档Lifecycle Hooks
  • 不要忘记注入您的依赖项,例如 myService 实例,我已将其添加到构造函数中。

测试

我建议您阅读Testing documentation。这是很多信息,但值得。下面是用于对组件进行单元测试的代码。

let comp: MyComponent ;
let fixture: ComponentFixture<MyComponent>;

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyComponent],
            providers: [
                { provide: MyService, useValue: {} }
            ]
        })
        .compileComponents(); 

    TestBed.compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    comp = fixture.componentInstance;
}));


it('initializes the component', fakeAsync(() => {
    var service = TestBed.get(MyService); // get your service
    service.get = () => {
            return Promise.resolve(); // you can pass data here if the service returns something
        };

    // here you could add an expect to validate component state before the call or service completes

    comp.ngOnInit(); // call ngOnInit
    tick(); // simulate the promise being resolved

    expect(service.get.toHaveBeenCalled);
    // here you could add an expect to validate component state after the service completes
}));

【讨论】:

  • 这行得通,谢谢。但是 Angular 是如何知道 ngOnInit 在继续运行我的单元测试之前完成了它的异步代码的呢?
  • 使用此代码,您在伪造服务调用,并且在伪造异步性。看看 beforeEach(async(() 和 it('...', fakeAsync(() 和 service.get = () => Promise.resolve() 和 tick() 调用。所有这些部分都扮演着他们的角色帮助您伪造组件的时间,使其体验就好像它已调用服务并以延迟方式响应。无论如何,tick() 模拟时间滴答作响。删除它会破坏测试。
【解决方案2】:

您的构造函数在测试之前执行,但是,构造函数的代码对服务进行异步调用,并在测试之后执行。

首先,您应该真正考虑将服务调用从构造函数中移开。

其次,在为组件编写测试时,您通常会监视服务调用并检查它们是否被调用,您实际上并没有进行调用,而是对其进行了模拟。查找“spyOn”的文档。

最后,如果您希望在测试之前发生某些事情,请查看“beforeEach”。无论如何,希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2022-09-29
    • 2016-03-19
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 2015-10-14
    相关资源
    最近更新 更多