模板中 observable 的最佳测试
Best testing for observable in template
这是被测组件:
@Component({
moduleId: module.id,
selector: 'my-search',
templateUrl: 'search.component.html',
styleUrls: ['search.component.css'],
})
export class SearchComponent implements OnInit {
things: Observable<Thing[]>;
private searchTerms = new Subject<string>();
constructor(private searchService: SearchService) { }
ngOnInit() {
this.things = this.searchTerms
.debounceTime(300)
.distinctUntilChanged()
.switchMap((term: string) => term
? this.searchService.search(term)
: Observable.of<Thing[]>([]));
}
search(term: string) {
this.searchTerms.next(term);
}
}
这里是 search.component.html:
<form (submit)="search()">
<input #searchBox (keyup)="search(searchBox.value)" />
<button type="submit">Search</button>
</form>
<div *ngIf="(things | async)?.length == 0; else searchResults">No things found.</div>
<ng-template #searchResults>
<div class="search-results">
<div *ngFor="let thing of things | async" class="search-result">
{{thing.id}}
</div>
</div>
</ng-template>
这是失败的测试:
it('should exhibit issue', fakeAsync(() => {
component.ngOnInit();
fixture.detectChanges();
component.search('123');
tick(300);
fixture.detectChanges();
component.things.subscribe((things) => {
// This fails. (I've tried various things, this is just the latest attempt.)
expect(dom.querySelectorAll('.search-results .search-result').length).toBe(1);
});
}));
Here's a plunk with what I've got so far.
无论我做什么,我都没有看到 DOM 发生变化。 fixture.detectChanges()
对 DOM 没有任何作用。
你如何测试 Observables?
这对我有用
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
fixture.detectChanges();
app.search('123');
tick(400);
fixture.detectChanges();
const searchNodes = fixture.debugElement.queryAll(By.css('.search-results .search-result')).map((item) => item.nativeElement);
expect(searchNodes.length).toBe(3);
终于成功了。您可以检查 the plunk (version 25),但这是浓缩的解决方案:
it('now works!', async(() => {
// Initialize the component.
component.ngOnInit();
fixture.detectChanges();
// The Observable is now initialized and we can now subscribe.
component.things.subscribe((things) => {
// Now that we've got the data, look for changes.
fixture.detectChanges();
// The DOM/view should now be up to date.
expect(dom.querySelectorAll('.search-results .search-result').length).toBe(1);
});
// Initiate stuff happening.
component.search('123');
}));
亮点:
- 使用
async
代替 fakeAsync
。
- 记得在您的订阅中调用
detectChanges
以便更新视图。
有一件事我想指出:subscribe
是在事件之前还是之后出现(示例中对 search
的调用)在这种情况下似乎并不重要。我把它放在前面作为预防措施,因为 'hot' Observable 可能会在 subscribe
注册之前处理事件;然后观察者将错过通知,回调将永远不会触发,测试将无效。
这是被测组件:
@Component({
moduleId: module.id,
selector: 'my-search',
templateUrl: 'search.component.html',
styleUrls: ['search.component.css'],
})
export class SearchComponent implements OnInit {
things: Observable<Thing[]>;
private searchTerms = new Subject<string>();
constructor(private searchService: SearchService) { }
ngOnInit() {
this.things = this.searchTerms
.debounceTime(300)
.distinctUntilChanged()
.switchMap((term: string) => term
? this.searchService.search(term)
: Observable.of<Thing[]>([]));
}
search(term: string) {
this.searchTerms.next(term);
}
}
这里是 search.component.html:
<form (submit)="search()">
<input #searchBox (keyup)="search(searchBox.value)" />
<button type="submit">Search</button>
</form>
<div *ngIf="(things | async)?.length == 0; else searchResults">No things found.</div>
<ng-template #searchResults>
<div class="search-results">
<div *ngFor="let thing of things | async" class="search-result">
{{thing.id}}
</div>
</div>
</ng-template>
这是失败的测试:
it('should exhibit issue', fakeAsync(() => {
component.ngOnInit();
fixture.detectChanges();
component.search('123');
tick(300);
fixture.detectChanges();
component.things.subscribe((things) => {
// This fails. (I've tried various things, this is just the latest attempt.)
expect(dom.querySelectorAll('.search-results .search-result').length).toBe(1);
});
}));
Here's a plunk with what I've got so far.
无论我做什么,我都没有看到 DOM 发生变化。 fixture.detectChanges()
对 DOM 没有任何作用。
你如何测试 Observables?
这对我有用
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
fixture.detectChanges();
app.search('123');
tick(400);
fixture.detectChanges();
const searchNodes = fixture.debugElement.queryAll(By.css('.search-results .search-result')).map((item) => item.nativeElement);
expect(searchNodes.length).toBe(3);
终于成功了。您可以检查 the plunk (version 25),但这是浓缩的解决方案:
it('now works!', async(() => {
// Initialize the component.
component.ngOnInit();
fixture.detectChanges();
// The Observable is now initialized and we can now subscribe.
component.things.subscribe((things) => {
// Now that we've got the data, look for changes.
fixture.detectChanges();
// The DOM/view should now be up to date.
expect(dom.querySelectorAll('.search-results .search-result').length).toBe(1);
});
// Initiate stuff happening.
component.search('123');
}));
亮点:
- 使用
async
代替fakeAsync
。 - 记得在您的订阅中调用
detectChanges
以便更新视图。
有一件事我想指出:subscribe
是在事件之前还是之后出现(示例中对 search
的调用)在这种情况下似乎并不重要。我把它放在前面作为预防措施,因为 'hot' Observable 可能会在 subscribe
注册之前处理事件;然后观察者将错过通知,回调将永远不会触发,测试将无效。