Angular 由于未定义FormArray控件,而必须定义控件,因此UT未通过

Angular 由于未定义FormArray控件,而必须定义控件,因此UT未通过,angular,jasmine,karma-jasmine,angular-unit-test,Angular,Jasmine,Karma Jasmine,Angular Unit Test,我有一个车间编辑组件(按顺序): 构建表单 检索要编辑的工作坊 使用车间值更新表单值 代码如下: ngOnInit() { this.buildForms(); this.initialize(); } async initialize(): Promise<void> { const id = this.route.snapshot.params.id; this.workshop = await this.workshopService.find(id);

我有一个车间编辑组件(按顺序):

  • 构建表单
  • 检索要编辑的工作坊
  • 使用车间值更新表单值
代码如下:

ngOnInit() {
  this.buildForms();
  this.initialize();
}

async initialize(): Promise<void> {
  const id = this.route.snapshot.params.id;

  this.workshop = await this.workshopService.find(id); // in real this is in a trycatch block
  this.updateFormValues();
}

buildForms(): void {
  this.form = ... // not important, this is not the problem
  this.discussesForm = this.formBuilder.group({
    array: this.formBuilder.array([], Validators.required),
  });
}

updateFormValues(): void {
  this.form.patchValue(this.workshop);
  this.workshop.ListDebates.forEach((discussion, index) => {
    this.addDiscussion();
    (this.discussesForm.get('array') as FormArray).at(index).patchValue({ // This line will throw error while UT.
      title: discussion.Title, description: discussion.Description, key: discussion.Key,
    });
  });
}

addDiscussion(): void {
  (this.discussesForm.get('array') as FormArray).push(this.formBuilder.group({
    title: [null],
    description: [null],
    key: [null],
  });
}
因此,上面的所有代码都可以正常工作,但我正在尝试对updateFormValues方法进行单元测试

这就是我所尝试的:

it('should update form values', () => {
  spyOn(component, 'addDiscussion');
  component.workshop = { Title: 'fake title', ListDebates: [
    { Key: 1, Title: 'fake', Description: 'fake' },
    { Key: 2, Title: 'fake', Description: 'fake' },
  ]} as any as IColabEvent;
  component.updateFormValues();
  expect(component.form.value.Title).toEqual('fake title'); // test OK
  expect((component.discussesForm.get('array') as FormArray).controls.length).toEqual(2); // test KO, expected 0 to be 2
  expect((component.discussesForm.get('array') as FormArray).at(0).value).toEqual(...); // test KO (not runned)
});
每次出现错误:无法读取未定义的属性“patchValue”(在updateFormValues方法中)

我尝试了很多方法(以及随机添加的方法,比如添加fixture.detectChanges()),但我没有找到解决方法

奇怪的是addDiscussion被调用了2次,所以我想知道为什么我的FormArray控件没有定义

我已经调用了console.log()一些东西,它看起来像是调用了addDiscussion,但并没有像它必须做的那样推动一个组


我重复我自己的话,但在真正的应用程序中,它是按预期工作的。

不是您的测试用例出了问题,而是您的代码出了问题。您无需先使用
addDiscussion
创建具有
null
值的对象,然后使用
patchValue
设置值。相反,在创建表单组本身时设置这些值。更改
addDiscussion
函数以接受
discussion
参数

addDiscussion(discussion={}):无效{
this.discussesForm.get('array').push(this.formBuilder.group({
标题:讨论。标题| |空,
description:discussion.description | | null,
键:讨论。键| |空
}));
}
然后在
updateFormValues
中,在
foreach
循环中,去掉
patchValue
代码,转而传递
讨论

this.workshop.listDiversations.forEach(discussion=>{
这是讨论;
});

除此之外,正如评论中已经提到的,不再需要监视
addDiscussion
,因为您的测试依赖于它。完成后,您的测试应该可以正常工作。

在第一行前面的
updateFormValues
中,执行
console.log(this.form)
并在它之后执行
调试器。使用
fit
运行测试,并立即打开开发人员工具(按F12)。
调试器
应该被触发,您可以在控制台中看到
this.form
的值。根据错误消息判断,我打赌
this.form
在该实例中没有及时定义。在测试中直接调用
updateFormValues
之前,可能没有调用
buildForms
此表单与
此表单不同。DiscusseForm
。在我的测试中,第一个expect pass意味着调用
buildForm
。我无法使用浏览器(仅cli)打开测试,因此调试有点困难。抱歉,我误解了。我会找到一种使用浏览器或某种调试方法进行调试的方法,以便使类似任务更容易。但我想我知道问题是什么。在测试中,您正在监视
addDiscussion
。这将只是为该函数创建一个“观察者”,而不是实际调用其实现。要同时拥有“watcher”和调用函数,您可以执行
spyOn(component,'addDiscussion')。和.callThrough()
。事实上,你可以扔掉这个间谍,我想你不需要它。哦,我用间谍来测试已经过了很多次了,但我现在不再需要它了。你是对的。是的!有时我不认为问题来自我的ts,而不是我的测试。。。我只需要添加
addEmptyDiscussion()
,因为用户可能会在表中添加空行^^^谢谢。随时:)无需创建新函数来添加空讨论。相反,如果只单独调用
addDiscussion
,而不使用任何参数,就足够了。我更新了答案,因此如果不将任何参数传递给
addDiscussion
,则
discussion
默认为空对象,从而在表中创建一个带有
null
值的空行。
it('should update form values', () => {
  spyOn(component, 'addDiscussion');
  component.workshop = { Title: 'fake title', ListDebates: [
    { Key: 1, Title: 'fake', Description: 'fake' },
    { Key: 2, Title: 'fake', Description: 'fake' },
  ]} as any as IColabEvent;
  component.updateFormValues();
  expect(component.form.value.Title).toEqual('fake title'); // test OK
  expect((component.discussesForm.get('array') as FormArray).controls.length).toEqual(2); // test KO, expected 0 to be 2
  expect((component.discussesForm.get('array') as FormArray).at(0).value).toEqual(...); // test KO (not runned)
});