运行Angular / Jasmine / Karma,我有一个组件,它使用一个服务来设置Observable'itements'数组的值。我使用异步管道显示它。效果很好。
现在,我正在尝试设置一个单元测试并让它通过,但我不确定我是否正确验证'items'数组是否获得了正确的值。
以下是相关组件.html和.ts:
export class ViperDashboardComponent implements OnInit, OnDestroy {
items: Observable<DashboardItem[]>;
constructor(private dashboardService: ViperDashboardService) { }
ngOnInit() {
this.items = this.dashboardService.getDashboardItems();
}
}
<ul class="list-group">
<li class="list-group-item" *ngFor="let item of items | async">
<h3>{{item.value}}</h3>
<p>{{item.detail}}</p>
</li>
</ul>
我的component.spec.ts:
beforeEach(() => {
fixture = TestBed.createComponent(ViperDashboardComponent);
component = fixture.componentInstance;
viperDashboardService =
fixture.debugElement.injector.get(ViperDashboardService);
mockItems = [
{ key: 'item1', value: 'item 1', detail: 'This is item 1' },
{ key: 'item2', value: 'item 2', detail: 'This is item 2' },
{ key: 'item3', value: 'item 3', detail: 'This is item 3' }
];
spy = spyOn(viperDashboardService, 'getDashboardItems')
.and.returnValue(Observable.of<DashboardItem[]>(mockItems));
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call getDashboardItems after component initialzed', () => {
fixture.detectChanges();
expect(spy.calls.any()).toBe(true, 'getDashboardItems should be called');
});
it('should show the dashboard after component initialized', () => {
fixture.detectChanges();
expect(component.items).toEqual(Observable.of(mockItems));
});
具体来说,我想知道:
1)我开始创建一个异步“it”测试,但是当它不起作用时感到惊讶。当我使用异步数据流时,为什么同步测试会起作用?
2)当我检查component.items与Observable.of(mockItems)的等价性时,我真的测试的是值是否相等?或者我只测试它们都是Observables?还有更好的方法吗?
答案 0 :(得分:3)
Angular提供了用于测试异步值的实用程序。您可以使用async
utility with the fixture.whenStable
方法或fakeAsync
utility with the tick()
function。然后使用DebugElement
,您实际上可以查询模板以确保正确加载值。
两种测试方法都有效。
将async
实用程序与whenStable
:
保持您的设置相同,您去那里很好。您需要添加一些代码才能获得列表的调试元素。在beforeEach
:
const list = fixture.debugElement.query(By.css('list-group'));
然后你可以深入到该列表并获取单个项目。我不太了解如何使用DebugElement
,因为这超出了这个问题的范围。点击此处了解详情:https://angular.io/guide/testing#componentfixture-debugelement-and-querybycss。
然后在你的单元测试中:
it('should get the dashboard items when initialized', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => { // wait for your async data
fixture.detectChanges(); // refresh your fake template
/*
now here you can check the debug element for your list
and see that the items in that list correctly represent
your mock data
e.g. expect(listItem1Header.textContent).toEqual('list item 1');
*/
}
}));
将fakeAsync
实用程序与tick
:
it('should get the dashboard items when initialized', async(() => {
fixture.detectChanges();
tick(); // wait for async data
fixture.detectChanges(); // refresh fake template
/*
now here you can check the debug element for your list
and see that the items in that list correctly represent
your mock data
e.g. expect(listItem1Header.textContent).toEqual('list item 1');
*/
}
}));
总而言之,请不要从模板中删除async
管道,以简化测试。 async
管道是一个很棒的实用工具,可以为你做很多清理工作,而且Angular团队为这个确切的用例提供了一些非常有用的测试工具。希望上述技术之一有效。这听起来像使用DebugElement
,其中一个上述实用程序将帮助你很多:)
答案 1 :(得分:1)
有一个用于测试可观察量的包jasmine-marbles
有了它,你可以写这样的测试:
...
spy = spyOn(viperDashboardService, 'getDashboardItems')
.and.returnValue(cold('-r|', { r: mockItems }));
...
it('should show the dashboard after component initialized', () => {
const items$ = cold('-r|', { r: mockItems });
expect(component.items).toBeObservable(items$);
});
但可能它不是最好的例子 - 我通常使用这个包来测试可观察链。例如,如果我在某些.map()
内部使用输入observable进行服务 - 我可以模拟原始的observable,然后创建一个新服务并与服务结果进行比较。
同样,async
和fakeAsync
都不适用于依赖于时间的可观察操作,但使用jasmine-marbles
,您可以将测试调度程序注入其中,它将像魅力一样工作,没有任何超时 - 即刻! Here我有一个如何注入测试调度程序的例子。