UT未通过,因为未定义FormArray控件,而控件必须被定义。

huangapple go评论68阅读模式
英文:

UT not pass due to undefined FormArray control while the control must be defined

问题

我有一个名为workshop-edit的组件,它按顺序执行以下操作:

- 构建表单
- 检索要编辑的研讨会
- 使用研讨会的值更新表单值

以下是代码:

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

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

  this.workshop = await this.workshopService.find(id); // 在实际应用中,这在trycatch块中
  this.updateFormValues();
}

buildForms(): void {
  this.form = ... // 不重要,这不是问题所在
  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({ // 这一行将在单元测试时引发错误。
      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],
  }));
}

workshop.ListDebates看起来像这样:

[
  {
    Key: 1,
    Title: 'title',
    Description: 'description',
  },
]

所以上面的所有代码都正常工作,但我正在尝试对updateFormValues方法进行单元测试。

这是我尝试过的内容:

it('应该更新表单值', () => {
  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'); // 测试通过
  expect((component.discussesForm.get('array') as FormArray).controls.length).toEqual(2); // 测试不通过,期望为2,实际为0
  expect((component.discussesForm.get('array') as FormArray).at(0).value).toEqual(...); // 测试不通过(未运行)
});

每次我都会收到错误消息:无法读取未定义的属性'patchValue'(在updateFormValues方法中)。

我尝试了很多方法(还有像添加fixture.detectChanges()这样的随机方法),但我找不到解决方法。

奇怪的是,addDiscussion被调用了2次,所以我不明白为什么我的FormArray控件未定义。

我已经使用console.log()打印了一些内容,看起来addDiscussion被调用了,但没有像应该的那样推送一个组。

我再次重申,在实际应用中,它按预期工作。
英文:

I have a workshop-edit component that (in order):

  • Build the form
  • Retrieve the workshop to edit
  • Update form values with workshop values

Here is the code:

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

async initialize(): Promise&lt;void&gt; {
  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) =&gt; {
    this.addDiscussion();
    (this.discussesForm.get(&#39;array&#39;) 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(&#39;array&#39;) as FormArray).push(this.formBuilder.group({
    title: [null],
    description: [null],
    key: [null],
  });
}

workshop.ListDebates look like:

[
  {
    Key: 1,
    Title: &#39;title&#39;,
    Description: &#39;description&#39;,
  },
]

So, all the code above works fine, but i'm trying to unit test the updateFormValues method.

This is what I tried:

it(&#39;should update form values&#39;, () =&gt; {
  spyOn(component, &#39;addDiscussion&#39;);
  component.workshop = { Title: &#39;fake title&#39;, ListDebates: [
    { Key: 1, Title: &#39;fake&#39;, Description: &#39;fake&#39; },
    { Key: 2, Title: &#39;fake&#39;, Description: &#39;fake&#39; },
  ]} as any as IColabEvent;
  component.updateFormValues();
  expect(component.form.value.Title).toEqual(&#39;fake title&#39;); // test OK
  expect((component.discussesForm.get(&#39;array&#39;) as FormArray).controls.length).toEqual(2); // test KO, expected 0 to be 2
  expect((component.discussesForm.get(&#39;array&#39;) as FormArray).at(0).value).toEqual(...); // test KO (not runned)
});

Everytime I get error: Cannot read property 'patchValue' of undefined (in the updateFormValues method).

I've tried lots of things (and random things like adding fixture.detectChanges()) but I don't find a way to fix it.

What is weird is that addDiscussion is called 2 times, so I wonder why my FormArray control is undefined.

I've console.log() some things and it look like addDiscussion is called but isn't pushing a group like it must does.

I repeat myself but in the real app it's working as intended.

答案1

得分: 1

与您的测试用例没有问题,实际上是您的代码存在问题。您无需首先使用 addDiscussion 创建一个具有 null 值的对象,然后使用 patchValue 来设置这些值。而是在创建表单组时即可设置这些值。将您的 addDiscussion 函数修改为接受 discussion 参数。

addDiscussion(discussion = {}): void {
    this.discussesForm.get('array').push(this.formBuilder.group({
        title: discussion.Title || null,
        description: discussion.Description || null,
        key: discussion.Key || null
    }));
}

然后在 updateFormValues 中的 foreach 循环中,删除 patchValue 代码并传递 discussion 参数。

this.workshop.ListDebates.forEach(discussion => {
    this.addDiscussion(discussion);
});

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

英文:

Rather than something being wrong with your test cases, it is in fact your code that has an issue. There is no need for you to use addDiscussion first to create an object with null values and then use patchValue to set the values. Instead, set the values as you create the form group itself. Change your addDiscussion function to accept the discussion parameter.

addDiscussion(discussion = {}): void {
    this.discussesForm.get(&#39;array&#39;).push(this.formBuilder.group({
        title: discussion.Title || null,
        description: discussion.Description || null,
        key: discussion.Key || null
    }));
}

Then in updateFormValues, in your foreach loop, get rid of the patchValue code and pass discussion instead.

this.workshop.ListDebates.forEach(discussion =&gt; {
    this.addDiscussion(discussion);
});

Apart from this, as already mentioned in the comments, the addDiscussion no longer needs to be spied upon since your test depends on it. Once this is done, your tests should be working.

huangapple
  • 本文由 发表于 2020年1月3日 20:08:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/59578386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定