Angular 使用对服务的取消公告更新值调用测试表单字段

Angular 使用对服务的取消公告更新值调用测试表单字段,angular,testing,jasmine,debounce,Angular,Testing,Jasmine,Debounce,我在一个组件中有一个表单字段(用户名),当更新时,它会调用一个服务来检查该值在数据库中的可用性。一切正常,但我似乎无法触发字段的更新功能 以下是组件代码的摘录: ngAfterViewInit(){ 此文件为.username.update .debounceTime(500)//在发出最后一个事件之前等待1/2秒 .distinctUntilChanged()//仅当与上一个值不同时才发出值 .subscribe((用户名:字符串)=>{ console.log('IN SUBSCRIBE')

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

以下是组件代码的摘录:

ngAfterViewInit(){
此文件为.username.update
.debounceTime(500)//在发出最后一个事件之前等待1/2秒
.distinctUntilChanged()//仅当与上一个值不同时才发出值
.subscribe((用户名:字符串)=>{
console.log('IN SUBSCRIBE');//{
if(response.isAvailable==false)
this.username.control.setErrors({'take':{value:this.username.value});
});
});
}
以下是组件模板中的字段:


用户名是必需的
{{禁止字符.错误}
用户名{{model.username}}被接受
这是我的测试:

it('should check for username availability',fakeAsync(()=>{
间谍(服务,'chkUser');
让输入=fixture.debugElement.query(By.css('#username')).nativeElement;
expect(input.value).toBe(“”);
expect(component.username.value).toBe(null);
input.value=user.username;
input.dispatchEvent(新事件('input'));
勾选(500);
expect(service.chkUser).tohavebeincall();
}));

最后一个
expect(service.chkUser).tohavebeencall()
就是导致我的测试失败的原因。我已经在一个应用程序中运行了这个组件,它按照预期工作,我只是想让测试通过。我尝试过设置输入值和分派事件、等待fixture检测更改或使用whenStable()、抛出承诺、使用fakeAsync而不是使用fakeAsync等多种组合。似乎什么都不管用。

因此,在经历了太多小时的挫折之后,我终于解决了我的问题。感谢那些评论让我朝着正确的方向思考,所以谢谢你@jornsharpe和@BorisLobanov

很明显,更新没有触发和
input.dispatchEvent(新事件('input'))不是这样的。这是第一条线索。我必须确保事件触发,以便呼叫服务

为了做到这一点,我已经从测试床上获取了组件的“componentInstance”

beforeach(()=>{
fixture=TestBed.createComponent(NewUserComponent);
组件=fixture.componentInstance;
fixture.detectChanges();
}
为了触发事件,我需要访问字段的底层,获取它的updateeventemitter并发出一些东西

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

component.username.update.emit(null);
这使我的字段运行附加到事件的取消公告订阅函数

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

经过几个小时的观察和重新创建模拟服务后,我终于在另一个问题中偶然发现了stackoverflow的答案,该问题说明在监视服务的方法时添加
.and.callThrough()
。天哪!我怎么会忘记(用手拍额头).subscribe无法运行,因为
spyOn(服务,'chkUser')
正在停止实际方法的执行,因此也无法观察到subscribe。在每个示例中,我都看到有人试图执行类似的操作,但他们从未添加
。和.callThrough()
给他们的间谍,所以我从来没有想到过,即使我以前无数次使用过它

无论如何,这是一个有效的测试:

it('should check for username availability',fakeAsync(()=>{
spyOn(服务,'chkUser')。和.callThrough();
让输入=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);
勾选(500);
expect(service.chkUser).tohavebeincall();
}));

你确定订阅已经创建,调用了
ngAfterViewInit
吗?是的,它在应用程序中工作,我已经手动测试过了。我的意思是在测试中。你有没有在日志中找到它的位置?运行调试器?这会不会像在应用程序中实例化一样创建组件:
fixture=TestBed.createComponent(NewUserComponent);
在我的
beforeach()
中是哪个。我如何测试在我的测试中是否调用了
ngAfterViewInit
?我不确定fakeAsync是否适用于这里,测试使用的是真正的dom,因此是真正的异步。试试async()相反,由于这个原因,真正的延迟可能是550次,而不是500次。