1

单元测试没跑通过

image.png

在跑单元测试的时候遇到了这个问题, 控制台报错:
An error was thrown in afterAll
Uncaught undefined thrown

浏览器:
image.png

1.由于没有报错组件的位置,当时第一时间想到的是,可能在某个单元测试afterAll()方法中,出现了问题。于是用全局搜索:afterAll。 但是并没有找到该关键字。

2.同时在mingao的电脑上,同样的代码,他那里跑单元测试并不会出现该问题。 只有我这里以及机器人跑的时候有这个问题。

3.之后也去之前的项目中找了afterAll 关键字, 也没有找到。

4.谷歌之后,发现有人遇到和我一样的问题,只不过我的是undefind. 他的是object.


image.png


尝试一

显示第一的是一个100多赞的答案,他说声明了两个beforeEach, 会造成竞争情况,导致运行失败。所以他把两个beforeEach 合并成同步了。

image.png
image.png


先简单描述async。

async函数的本质就是返回了一个promise对象,在这个函数里里我们加上await后,即使调用的是异步代码,它也会变成类似于同步,只有让这个异步代码执行完后,才会执行下面的同步代码来执行。



首先不说是否能解决问题。 实际上,合并与不合并,只是angular官方推荐的两种不同的设置:

1.分开设置:
image.png
image.png

2.组合设置:

image.png


所以这只是两种不同的写法。

我估计他只是,忘了写await了,导致两个beforeEach同步执行了。
image.png

研究了一下这种方法,最终不起作用。


尝试二

第二个答案:

在单元测试中添加这个: 在每个单元测试后销毁夹具

 afterEach(() => {
    fixture.destroy();
  });

image.png


由于是跑全局单元测试时报的错,所以我并不知道在哪个单元测试中添加这个项。

好在目前组件并不算多,于是我挨个尝试了一下。

最后在menu.component组件中添加了该项后。单元测试没有再报错。

研究原因
注意到该组件和别的地方不一样的是, 添加了teardown: { destroyAfterEach: false }

image.png

之前也介绍过:
angular13 新版本中每次测试的时候会清理DOM, 该项默认为true。 设置为false之后我们可以在fit下, 观察到v层的变化。 否则看见的是空白。
image.png

所以我猜想是设置了destroyAfterEach: false 才导致的上面的报错。

于是我删除了teardown: { destroyAfterEach: false } 和
afterEach(() => {fixture.destroy();})

再进行测试。

结果和想象中的一样,没有报错。
image.png

所以结论出来了:目前使用 teardown: { destroyAfterEach: false }, 需要 同时使用afterEach(() => {fixture.destroy();}) 来销毁夹具。

但令我疑惑的是,其实之前只单单使用了destroyAfterEach: false, 当时并没有报错。 本文中的报错其实是在前几天才发现。 可能还受到其他的一些因素影响。

ngmodel在fromGroup下需要添加standalone

这个报错说明的很清楚,ngmodel不能用于fromGroup下。 若需要使用,需要添加:[ngModelOptions] = "standalone: true"

lQLPJxarkWdjbtXNAZ_NBBGwZTbUjUzPB-sDGinNy0DfAA_1041_415.png

添加之后运行成功.

第三方标签不适配fromControl

在开发 角色选择组件 的时候控制台发现了如下的报错。

image.png

后来经过排查后发现,是因为使用fromControl绑定了第三方标签thy-custom-select的问题。

image.png

在官方的文档中,使用示例用的是[(ngModel)] = "" 来绑定变量。

但是经过多次测试之后,若使用[fromControl]="" 绑定, 则会出现如上的报错。

后来只能改一下角色选择组件。

选择ngModel控制变量。ngModelChange($event)来接收变量的改变。
image.png

总的来说和以前相比,即是多了一个变量,来作为中间变量控制fromControl

以下代码前面带+号的为新增

export class UserRoleSelectComponent implements OnInit, ControlValueAccessor {

  selected = new FormControl;

 +  roleType!: RoleType;

  registerOnChange(fn: any): void {
    this.selected.valueChanges
      .subscribe(data => fn(data));
  }

  registerOnTouched(fn: any): void {
  }

  writeValue(obj: number): void {
    this.selected.setValue(obj);
+    this.roleType = obj as RoleType;
  }

+  ngModelChange($event : RoleType) {
+    this.selected.setValue($event);
+  }

}

weiweiyi
1k 声望123 粉丝