-1

我在组件中有一个表单字段(用户名),当更新时调用服务来检查数据库中值的可用性。一切正常,但我似乎无法触发该字段的更新功能。

以下是组件代码的摘录:

 ngAfterViewInit() {
   this.username.update
    .debounceTime(500) // wait 1/2 second before emitting last event
    .distinctUntilChanged() // only emit value if different from last value
    .subscribe((username: string) => {

      console.log('IN SUBSCRIBE'); // <==== This is not being reached in the tests

      let data = { type: 'username', val: username };
      this._loginSrvc.chkUser(data).subscribe((response: IChkUserResponse)=>{
        if(response.isAvailable === false)
          this.username.control.setErrors({'taken': {value: this.username.value}});
      });
    });
 }

这是组件模板中的字段:

<mat-form-field fxFlex>
  <input matInput placeholder="Username" aria-label="Username" forbiddenCharacters=';"\\\/\[\]\{\}\(\)' required [(ngModel)]="model.username" #username="ngModel" id="username" name="username">
  <mat-error *ngIf="newUserFrm.hasError('required','username')">Username is required</mat-error>
  <mat-error *ngIf="newUserFrm.hasError('forbiddenCharacters','username')">{{forbiddenChars.error}}</mat-error>
  <mat-error *ngIf="newUserFrm.hasError('taken','username')">The username <em>{{model.username}}</em> is taken</mat-error>
</mat-form-field>

这是我的测试:

it('should check for username availability',fakeAsync(()=>{
  spyOn(service,'chkUser');

  let input = fixture.debugElement.query(By.css('#username')).nativeElement;
  expect(input.value).toBe('');
  expect(component.username.value).toBe(null);

  input.value = user.username;
  input.dispatchEvent(new Event('input'));

  tick(500);

  expect(service.chkUser).toHaveBeenCalled();
}));

最后expect(service.chkUser).toHaveBeenCalled()是导致我的测试失败的原因。我已经在应用程序中运行了这个组件,它按预期工作我只是想让测试通过。我已经尝试了很多设置输入值和调度事件的组合,等待夹具检测更改或使用 whenStable(),抛出一个承诺,使用 fakeAsync 而不是使用 fakeAsync。似乎没有任何效果。

4

1 回答 1

1

因此,经过太多小时的挫折,我发现了我的问题。多亏了那些评论让我朝着正确的方向思考,所以谢谢@jonrsharpe 和@BorisLobanov。

所以显然更新EventEmitter没有触发,input.dispatchEvent(new Event('input'));也没有触发。这是第一个线索。我必须确保事件触发才能调用服务。

为了做到这一点,我已经从 TestBed 中获取了组件的“componentInstance”。

 beforeEach(() => {
   fixture = TestBed.createComponent(NewUserComponent);
   component = fixture.componentInstance;
   fixture.detectChanges();
 }

为了让事件触发,我需要访问该字段的底层ngModel获取它的更新 EventEmitter 并发出一些东西。

所以在我的测试中,我添加了这一行:

 component.username.update.emit(null);

这让我的字段运行附加到事件的去抖动订阅功能。

然而,这并没有解决整个问题,运行测试给了我一个无法subscribe在未定义时执行。导致错误的订阅是附加到服务调用的订阅this._loginSrvc.chkUser(data).subscribe((...)=>{...})

经过数小时摆弄 Observables 并重新创建我的模拟服务后,我终于在另一个问题中偶然发现了一个 stackoverflow 答案,该答案表明.and.callThrough()在监视服务方法时要记住添加很简单。我的天啊!我怎么会忘记这一点(用手拍打额头)。订阅不起作用,因为它spyOn(service,'chkUser')正在停止实际方法的执行,所以也没有 Observable 可以订阅。在我看到的每个尝试做类似事情的示例中,他们从未将其添加.and.callThrough()到他们的间谍中,所以即使我以前无数次使用过它,我也从未想过。

无论如何,这是工作测试:

it('should check for username availability',fakeAsync(()=>{
  spyOn(service,'chkUser').and.callThrough();

  let input = fixture.debugElement.query(By.css('#username')).nativeElement;
  expect(input.value).toBe('');
  expect(component.username.value).toBe(null);

  input.value = user.username;

  component.username.update.emit(null);

  tick(500);

  expect(service.chkUser).toHaveBeenCalled();
}));
于 2018-02-23T14:50:34.510 回答