从服务返回的 Observable 的单元测试值(使用异步管道)
Unit testing value of Observable returned from service (using async pipe)
运行 Angular/Jasmine/Karma,我有一个组件使用服务来设置 Observable 'items' 数组的值。我使用异步管道显示它。效果很好。
现在,我正在尝试设置单元测试并让它通过,但我不确定我是否正确验证了 '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) 的等价性时,我是否真的在测试这些值是否相等?还是我只是在测试它们都是 Observable?有没有更好的方法?
Angular 提供用于测试异步值的实用程序。你可以使用async
utility with the fixture.whenStable
method or the 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', fakeAsync(() => {
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
并且上述实用程序之一会对您有很大帮助:)
有一个用于测试 observables 的包 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()
ing 内部使用输入可观察对象进行操作 - 我可以模拟原始可观察对象,然后创建一个新的并与服务结果进行比较。
此外,async
和 fakeAsync
都不能处理依赖于时间的可观察操作,但是使用 jasmine-marbles
你可以将测试调度程序注入它们,它会像一个魅力一样工作,没有任何超时 - 立即! Here 我有一个如何注入测试调度程序的示例。
运行 Angular/Jasmine/Karma,我有一个组件使用服务来设置 Observable 'items' 数组的值。我使用异步管道显示它。效果很好。
现在,我正在尝试设置单元测试并让它通过,但我不确定我是否正确验证了 '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) 的等价性时,我是否真的在测试这些值是否相等?还是我只是在测试它们都是 Observable?有没有更好的方法?
Angular 提供用于测试异步值的实用程序。你可以使用async
utility with the fixture.whenStable
method or the 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', fakeAsync(() => {
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
并且上述实用程序之一会对您有很大帮助:)
有一个用于测试 observables 的包 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()
ing 内部使用输入可观察对象进行操作 - 我可以模拟原始可观察对象,然后创建一个新的并与服务结果进行比较。
此外,async
和 fakeAsync
都不能处理依赖于时间的可观察操作,但是使用 jasmine-marbles
你可以将测试调度程序注入它们,它会像一个魅力一样工作,没有任何超时 - 立即! Here 我有一个如何注入测试调度程序的示例。