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

eri*_*rin 3 unit-testing jasmine typescript karma-runner angular

运行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();
    }
}
Run Code Online (Sandbox Code Playgroud)
    <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>
Run Code Online (Sandbox Code Playgroud)

而我的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));
    });
Run Code Online (Sandbox Code Playgroud)

具体来说,我想知道:

1)我开始创建一个异步的“ it”测试,但是当它不起作用时感到惊讶。在使用异步数据流时,为什么同步测试有效?

2)当我检查component.items与Observable.of(mockItems)的等效性时,我是否真的在测试这些值是否相等?还是我只是在测试它们都是可观察的?有没有更好的办法?

vin*_*nce 6

Angular提供了用于测试异步值的实用程序。您可以将async实用程序与fixture.whenStable方法一起使用,或者将fakeAsync实用程序与tick()函数一起使用。然后,使用DebugElement,您可以实际查询您的模板,以确保正确加载了这些值。

两种测试方法都可以使用。

通过以下方式使用async实用程序whenStable

保持您的设置不变,您可以继续进行。您需要添加一些代码来获取列表的debug元素。在您的beforeEach

const list = fixture.debugElement.query(By.css('list-group'));
Run Code Online (Sandbox Code Playgroud)

然后,您可以深入到该列表并获取单个项目。我不会太过深入地介绍如何使用,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');
           */
        }
    }));
Run Code Online (Sandbox Code Playgroud)

通过以下方式使用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');
        */
        }
    }));
Run Code Online (Sandbox Code Playgroud)

因此,总而言之,请勿async仅从模板中删除管道以简化测试。该async管道是一个非常实用和做了很多清理你,加上角队已经提供了一些非常有用的测试UTILITES这个确切的用例。希望以上技术之一能奏效。听起来像是使用DebugElement,上述工具之一将为您提供很多帮助:)