【问题标题】:Angular testing - ngBootstraps typeahead角度测试 - ngBootstraps 预输入
【发布时间】:2019-03-25 16:00:48
【问题描述】:

我目前正在使用 ngBootstrap 的自动完成机制(预先输入)。现在我想对输入事件的每个序列是否调用方法进行单元测试。我的测试用例的错误目前是:Cannot read property 'pipe' of undefined

HTML:

<input id="locationEdit" type="text" class="form-control"
         [(ngModel)]="user.location" name="location [ngbTypeahead]="search"/>

组件:

public ngOnInit() {
    this.search = (text$: Observable<string>) =>
      text$.pipe(
        tap(() => {
          this.isSearching = true;
          this.searchFailed = false;
        }),
        debounceTime(750),
        distinctUntilChanged(),
        switchMap(term =>
          this.cityService.getLocation(term).pipe(
            tap((response) => {
              this.searchFailed = response.length === 0;
              this.isSearching = false;
            })))
      );
  }

spec.ts

  it('should call spy on city search', fakeAsync(() => {
    component.user = <User>{uid: 'test', username: 'mleko', location: null, description: null};
    const spy = (<jasmine.Spy>cityStub.getLocation).and.returnValue(of['München Bayern']);

    fixture.detectChanges();
    const compiled: DebugElement = fixture.debugElement.query(By.css('#locationEdit'));
    compiled.nativeElement.value = 'München';
    compiled.nativeElement.dispatchEvent(new Event('input'));

    tick(1000);
    fixture.detectChanges();

    expect(spy).toHaveBeenCalled();
  }));

有人可以帮我正确地模拟 this.search 吗?

编辑

通过@dmcgrandle 的精彩建议,我不需要渲染 HTML 并模拟输入事件来检查预输入是否正常工作。我宁愿制作一个 Observable,它会发出值并将其分配给函数。一种方法是:

  it('should call spy on city search', fakeAsync(() => {
    const spy = (<jasmine.Spy>cityStub.getLocation).and.returnValue(of['München Bayern']);

    component.ngOnInit();
    const textMock = of(['M', 'Mün', 'München']).pipe(flatMap(index => index));

    component.search(textMock);

    tick();

    expect(spy).toHaveBeenCalled();
  }));

但问题仍然是,component.search 没有调用间谍。在 switchMap 运算符的搜索函数中,我添加了一个 console.log 以查看函数是否发出了值。但事实并非如此。

【问题讨论】:

  • 错误来自text$.pipe( 放置调试点或console.log 并检查那里的值是什么
  • 尝试添加spyOn(component, 'search').and.returnValue(of('some string'));
  • @HDJEMAI 我已经尝试过了,但它让我搜索方法在该组件上不存在..

标签: angular rxjs karma-jasmine ng-bootstrap angular-test


【解决方案1】:

我认为您实际上不想在测试期间调用任何 ngBootstrap 代码 - 毕竟您想要对代码进行单元测试,而不是他们的代码。 :)

因此,我建议通过设置您自己的定时 Observable 并使用它调用您的函数来模拟用户实际打字。也许模拟每 100 毫秒发送一个字符。像这样的:

it('should call spy on city search', fakeAsync(() => {
    component.user = <User>{uid: 'test', username: 'mleko', location: null, description: null};
    // Change next line depending on implementation of cityStub ...
    const spy = spyOn(cityStub, 'getLocation').and.returnValue(of('München Bayern'));

    fixture.detectChanges();
    let inputTextArray = ['M', 'Mü', 'Mün', 'Münc', 'Münch', 'Münche', 'München'];
    let textMock$ : Observable<string> = interval(100).pipe(take(7),map(index => inputTextArray[index]));
    component.search(textMock$);
    tick(1000);
    expect(spy).toHaveBeenCalled();
}));

更新:

我在这里整理了一个 stackblitz 来测试一下:https://stackblitz.com/edit/stackoverflow-question-52914753(打开左侧的 app 文件夹并单击 my.component.spec.ts 以查看测试文件)

一旦我把它放进去,问题就很明显了——observable 没有被订阅,因为订阅似乎是由 ngBootstrap 完成的,所以为了测试我们需要显式订阅。这是我建议的新规范(取自 stackblitz):

it('should call spy on city search', fakeAsync(() => {
    const cityStub = TestBed.get(CityService);
    const spy = spyOn(cityStub, 'getLocation').and.returnValue(of('München Bayern'));

    fixture.detectChanges();
    let inputTextArray = ['M', 'Mü', 'Mün', 'Münc', 'Münch', 'Münche', 'München'];
    let textMock$ : Observable<string> = interval(100).pipe(take(7),map(index => inputTextArray[index]));
    component.search(textMock$).subscribe(result => {
         expect(result).toEqual('München Bayern');
    });
    tick(1000);
    expect(spy).toHaveBeenCalled();
}));

【讨论】:

  • 感谢您的更新 - 我根据您的反馈更新了我的回复。
  • 感谢您的回答并祝贺赏金。^^
【解决方案2】:

请尝试在服务中移动 observable:

组件:

this.cityService.text$.pipe

服务:

export class CityService {
private _subject = null;
text$ = null;

constructor(private _httpClient: HttpClient) {
    this.init();
}

init() {
    this._subject = new BehaviorSubject<any>({});
    this.text$ = this._subject.asObservable();
}

如果您需要更多详细信息,我可以扩展我的答案。

【讨论】:

  • 我现在很困惑。^^ 这是对设计模式的建议,因为这样的功能应该更适合服务而不是组件?或者这是我的单元测试失败的解决方法?有兴趣了解详情...:)
猜你喜欢
  • 2017-04-25
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
  • 2020-04-01
  • 2016-11-04
  • 2016-01-14
相关资源
最近更新 更多