从父组件传递到子组件的Angular 2单元测试数据 [英] Angular 2 unit testing data passed from parent component to child component
问题描述
我对我看到(很少)从父组件传递到子组件的数据测试示例的方式有疑问.当前,在
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.albumCover
从Story
控制器绑定到AlbumCover
的image
属性.这一切都运行良好.现在进行测试:
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屋!