代码之家  ›  专栏  ›  技术社区  ›  MarcoLe

角度测试-ngbootstraps typeahead

  •  6
  • MarcoLe  · 技术社区  · 7 年前

    我目前使用的是ngbootstrap的自动完成机制(typeahead)。现在,我想对输入事件的每个序列调用方法进行单元测试。我的测试用例上的错误当前为: 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;
                })))
          );
      }
    

    规格

      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();
      }));
    

    有人能帮我嘲弄一下这个吗?按比例搜索?

    编辑

    通过@dmcgrandle的出色建议,我不需要呈现HTML并模拟输入事件,以检查typeahead是否工作正常。我宁愿做一个可观察的,它发出值并将其分配给函数。一种方法是:

      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 查看函数是否发出值。但事实并非如此。也许有人能帮我解决问题。:)

    2 回复  |  直到 7 年前
        1
  •  2
  •   dmcgrandle    7 年前

    我认为您实际上不想在测试期间调用任何ngbootstrap代码——毕竟您希望对代码进行单元测试,而不是对它们进行单元测试。:)

    因此,我建议通过设置一个自己的可观察时间,并用它调用您的函数来模拟用户实际键入的内容。也许每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
  •  1
  •   mruanova    7 年前

    请尝试在服务内移动可观察的:

    组件:

    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();
    }
    

    如果你需要更多的细节,我可以扩大我的答案。

    推荐文章