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

问题描述

我对我看到的(极少数)测试从父组件传递到子组件的数据示例的方式有疑问.目前,在 Angular2 文档,他们正在通过检查子组件的 dom 值来测试数据是否已从父组件传递到子组件.我对这种方法的问题是它迫使父规范了解子组件的 html 结构.父组件的工作只是将数据传递给子组件.一个例子……

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 单元测试数据从父组件传递到子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-03 20:05
查看更多