从父组件传递到子组件的Angular 2单元测试数据 [英] Angular 2 unit testing data passed from parent component to child component

查看:89
本文介绍了从父组件传递到子组件的Angular 2单元测试数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对我看到(很少)从父组件传递到子组件的数据测试示例的方式有疑问.当前,在

I have a question about the way I've seen (the very few) examples of testing of data passed down from a parent component into a child component. Currently, in the Angular2 docs, they're testing to see if data has been passed down from a parent component to a child by inspecting the dom values of the child component. The issue that I have with this approach is that it forces the parent's spec to know the html structure of the child component. The parent component's job is just to pass data into the child. An example...

我有一个故事组件,如下所示:

I have a Story Component as follows:

'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);
    }
}

请注意在@Component装饰器的directives数组中它如何具有AlbumCover组件依赖性.

Notice how it has an AlbumCover Component dependency in the directives array in the @Component decorator.

这是我的故事模板:

<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控制器绑定到AlbumCoverimage属性.这一切都运行良好.现在进行测试:

Notice the <album-cover [image]="story.albumCover" [title]="story.title"></album-cover> line where I'm binding the story.albumCover from the Story controller to the image property of the AlbumCover. This is all working perfectly. Now for the test:

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>标记,然后检查<img>的DOM属性.对我来说,这种期望应该在album-cover.spec.ts之内-而不是story.spec.ts.

Look at my describe('album cover'.... The way I'm passing this expectation is that I'm finding the <album-cover> element, then finding the <img> tag inside of it, then checking the <img>'s DOM attributes. To me, this expection should be inside of the album-cover.spec.ts - NOT the story.spec.ts.

我的问题:是否可以测试父组件是否将数据传递给子组件而不依赖于读取dom值?

My question is: is there a way to test if a parent component passed data into a child component without relying on reading dom values?

推荐答案

您可以使用overrideTemplate传递仅用于测试的视图.

You can use overrideTemplate to pass a view just for the test.

   return tcb
        .overrideTemplate(AlbumCover, '<div>{{valueFromParent}}</div>')
        .overrideProviders(Story, [

这篇关于从父组件传递到子组件的Angular 2单元测试数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆