Angular 2单元测试数据从父组件传递到子组件

时间:2016-06-23 04:48:50

标签: unit-testing angular components

我对我看过(极少数)测试从父组件传递到子组件的数据的方法有疑问。目前,在Angular2 docs中,他们正在测试是否通过检查子组件的dom值来查看数据是否已从父组件传递给子组件。我对这种方法的问题在于它强制父类的规范知道子组件的html结构。父组件的工作只是将数据传递给子。一个例子......

我有一个故事组件如下:

'use strict';

import {Component, OnInit, Input} from '@angular/core';
import {StoryService} from '../../services/story.service';
import {StoryModel} from '../../models/story-model';
import {AlbumCover} from './album-cover/album-cover';
import {Author} from "./author/author";
import {StoryDuration} from "./story-duration/story-duration";

@Component({
    selector: 'story',
    templateUrl: 'build/components/story/story.html',
    providers: [StoryService],
    directives: [AlbumCover, Author, StoryDuration]
})

export class Story implements OnInit {
    @Input('id') id:number;
    public story:StoryModel;

    constructor(private storyService:StoryService) {}

    ngOnInit() {
        this.getStory();
    }

    private getStory() {
        this.storyService.getStory(this.id).subscribe(story => this.story = story);
    }
}

请注意AlbumCover装饰器中directives数组中的@Component组件依赖关系。

这是我的故事模板:

<div *ngIf="story">
    <album-cover [image]="story.albumCover" [title]="story.title"></album-cover>
    <div class="author-duration-container">
        <author [avatar]="story.author.avatar" [name]="story.author.name"></author>
        <story-duration [word-count]="story.wordCount"></story-duration>
    </div>
</div>

请注意<album-cover [image]="story.albumCover" [title]="story.title"></album-cover>行,story.albumCoverStory控制器绑定到image的{​​{1}}属性。这一切都很完美。现在进行测试:

AlbumCover

看看我的import {provide} from '@angular/core'; import {beforeEach, beforeEachProviders, describe, expect, injectAsync, it, setBaseTestProviders, resetBaseTestProviders} from '@angular/core/testing'; import {HTTP_PROVIDERS} from '@angular/http'; import {BROWSER_APP_DYNAMIC_PROVIDERS} from "@angular/platform-browser-dynamic"; import {TEST_BROWSER_STATIC_PLATFORM_PROVIDERS, ADDITIONAL_TEST_BROWSER_PROVIDERS} from '@angular/platform-browser/testing'; import {ComponentFixture, TestComponentBuilder} from '@angular/compiler/testing'; import {Observable} from 'rxjs/Observable'; // TODO: this pattern of importing 'of' can probably go away once rxjs is fixed // https://github.com/ReactiveX/rxjs/issues/1713 import 'rxjs/add/observable/of'; resetBaseTestProviders(); setBaseTestProviders( TEST_BROWSER_STATIC_PLATFORM_PROVIDERS, [BROWSER_APP_DYNAMIC_PROVIDERS, ADDITIONAL_TEST_BROWSER_PROVIDERS] ); import {Story} from './story'; import {StoryModel} from '../../models/story-model'; import {StoryService} from '../../services/story.service'; var mockStory = { id: 1, title: 'Benefit', albumCover: 'images/placeholders/story-4.jpg', author: { id: 2, name: 'Brett Beach', avatar: 'images/placeholders/author-1.jpg' }, wordCount: 4340, content: '<p>This is going to be a great book! I <strong>swear!</strong></p>' }; class MockStoryService { public getStory(id):Observable<StoryModel> { return Observable.of(mockStory); } } describe('Story', () => { var storyFixture, story, storyEl; beforeEachProviders(() => [ HTTP_PROVIDERS ]); beforeEach(injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) => { return tcb .overrideProviders(Story, [ provide(StoryService, { useClass: MockStoryService }) ]) .createAsync(Story) .then((componentFixture:ComponentFixture<Story>) => { storyFixture = componentFixture; story = componentFixture.componentInstance; storyEl = componentFixture.nativeElement; componentFixture.detectChanges(); }); })); describe(`ngOnInit`, () => { describe(`storyService.getStory`, () => { it(`should be called, and on success, set this.story`, () => { spyOn(story.storyService, 'getStory').and.callThrough(); story.ngOnInit(); expect(story.storyService.getStory).toHaveBeenCalled(); expect(story.story.title).toBe('Benefit'); }); }); }); it('should not show the story component if story does not exist', () => { story.story = null; storyFixture.detectChanges(); expect(storyEl.children.length).toBe(0); }); it('should show the story component if story exists', () => { story.story = mockStory; storyFixture.detectChanges(); expect(storyEl.children.length).not.toBe(0); }); describe('story components', () => { beforeEach(() => { story.story = mockStory; storyFixture.detectChanges(); }); describe('album cover', () => { var element, img; beforeEach(() => { element = storyEl.querySelector('album-cover'); img = element.querySelector('img'); }); it(`should be passed the story albumCover and title to the album cover component`, () => { expect(img.attributes.src.value).toBe(mockStory.albumCover); expect(img.attributes.alt.value).toBe(mockStory.title); }); }); describe('author', () => { var element, img, nameEl; beforeEach(() => { element = storyEl.querySelector('author'); img = element.querySelector('img'); nameEl = element.querySelector('.name'); }); it(`should be passed the author name and avatar`, () => { expect(img.attributes.src.value).toBe(story.story.author.avatar); expect(img.attributes.alt.value).toBe(story.story.author.name); expect(nameEl.innerText).toBe(story.story.author.name); }); }); describe('story duration', () => { var element; beforeEach(() => { element = storyEl.querySelector('.story-duration'); }); it(`should be passed the word count to generate the total read time`, () => { story.story.wordCount = 234234; storyFixture.detectChanges(); expect(element.innerText).toBe(`852 min read`); }); }); }); }); 。我传递这种期望的方式是我找到describe('album cover'...元素,然后在其中找到<album-cover>标记,然后检查<img>的DOM属性。对我来说,这次考察应该在<img>之内 - 而不是album-cover.spec.ts

我的问题是:有没有办法测试父组件是否将数据传递到子组件而不依赖于读取dom值?

1 个答案:

答案 0 :(得分:9)

您可以使用DELETE T1 FROM @T T1 INNER JOIN ( SELECT Mobile, MAX(Dated) as Dated FROM @T GROUP BY Mobile ) T2 ON T1.Mobile = T2.Mobile WHERE T1.Dated < T2.Dated 传递仅供测试的视图。

overrideTemplate