模板中 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 注册之前处理事件;然后观察者将错过通知,回调将永远不会触发,测试将无效。