从服务返回的Observable的单元测试值(使用异步管道)

时间:2018-01-18 18:46:20

标签: angular unit-testing typescript jasmine karma-runner

运行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?还有更好的方法吗?

2 个答案:

答案 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,然后创建一个新服务并与服务结果进行比较。

同样,asyncfakeAsync都不适用于依赖于时间的可观察操作,但使用jasmine-marbles,您可以将测试调度程序注入其中,它将像魅力一样工作,没有任何超时 - 即刻! Here我有一个如何注入测试调度程序的例子。