错误:使用同步 Observable 测试 Angular 组件时的 ExpressionChangedAfterItHasBeenCheckedError

Pic*_*cci 4 rxjs angular angular-test

我有一个简单的组件,它使用一个返回 Observable 的服务。此类 Observable 用于通过 控制 html 元素的创建*ngIf

这里的代码

@Component({
  selector: 'hello',
  template: `<h1 *ngIf="stuff$ | async as obj">Hello {{obj.name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements AfterViewInit {
  constructor(
    private myService: MyService,
  ) {}

  stuff$;

  ngAfterViewInit() {
    this.stuff$ = this.myService.getStuff();
  }
}


@Injectable({
  providedIn: 'root'
})
export class MyService {
  getStuff() {
    const stuff = {name: 'I am an object'};
    return of(stuff).pipe(delay(100));
  }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,getStuff()返回和 Observable 带有 adelay并且一切都按预期工作。

现在我删除了delay,我在控制台上收到以下错误

错误:ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。以前的值:'ngIf: null'。当前值:'ngIf: [object Object]'。

这是一个重现案例的 Stackbliz。

这是不可避免的吗?有没有办法避免这个错误?这个小例子复制了一个真实世界的例子,其中MyService查询后端,所以延迟。

这对我来说很重要,因为我希望能够以模拟真实后端的同步方式运行测试,并且当我尝试这种方法时,我得到了相同的结果。

mar*_*tin 8

该错误基本上是说一个更改触发了另一个更改。它delay(100)之所以发生,是因为 RxJS 是严格顺序的,并且订阅是同步发生的。如果您使用delay(100)(即使您只使用delay(0)它,它也会使发射异步,因此它发生在另一个帧中并被另一个更改检测周期捕获。

避免这种情况的一个非常简单的方法是使用setTimeout()或包装对变量的赋值NgZone.run()

或者更多的“接收方式”正在使用subscribeOn(async)运算符:

import { asyncScheduler } from 'rxjs';
import { observeOn } from 'rxjs/operators';

...

return of(stuff).pipe(
  observeOn(asyncScheduler)
);
Run Code Online (Sandbox Code Playgroud)