Jest.js 在ngAfterViewInit之后,夹具未更新

carvr3hs  于 12个月前  发布在  Jest
关注(0)|答案(1)|浏览(105)

我正在用Jest测试我的Angular应用程序。下面是我的组件的简化模板:

<div *ngIf="viewInitialized">
  <button
    class="button"
    (click)="onLogoutClicked()"
  >
    {{ changeTokenText }}
  </button>
</div>

下面是该组件的简化代码:

@Component({
  selector: 'logout-qr-page',
  templateUrl: './logout.component.html',
  styleUrls: ['./logout.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LogoutComponent implements OnInit, AfterViewInit {
  viewInitialized = false;

  constructor(
    ...
  ) {}

  ngOnInit(): void {
    ...
  }

  ngAfterViewInit(): void {
    ...
    this.viewInitialized = true;
  }

  onLogoutClicked(): void {
    ...
  }
}

我的测试是:

describe('TsdLogoutComponent', () => {
  let component: LogoutComponent;
  let fixture: ComponentFixture<LogoutComponent>;
  let debugElement: DebugElement;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [LogoutComponent],
      imports: [
       ...
      ],
      providers: [
       ...
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(LogoutComponent);
    component = fixture.componentInstance;
    debugElement = fixture.debugElement;
    fixture.detectChanges();
  });

  it('should create the component', () => {
    expect(component).toBeTruthy();
  });

  it('should correctly render default state', () => {
    expect(component.viewInitialized).toBe(true); // PASS
    expect(debugElement.query(By.css('.button'))).not.toBeNull(); // FAIL
  });
});

所以你可以看到viewInitialized是true,但是没有类为button的元素。调用ViewInit后,它更改了值,但没有更新灯具。
我试过:

  • 多次调用fixture.detectChanges(),只在测试内部调用(不在每次之前调用),在每次之前和测试中调用;
  • 使用await fixture.whenStable(),与fixture.detectChanges()一起使用;
  • 使用fixture.autoDetectChanges()

什么起作用了:如果我手动执行component.viewInitialized = true(在测试中),如果我在组件中使用信号(viewInitialized = signal(false) => viewInitialized.set(true)),或者如果我在ngAfterViewInit中调用cdRef.detectChanges(),则测试开始通过。但我真的不想使用任何这些-在我的项目中,没有信号或cdRef,一切都能完美地工作,我希望它在测试中也能工作,或者至少我想了解这种行为的原因。

gojuced7

gojuced71#

我遇到了同样的问题,这个问题最有可能是因为:

changeDetection: ChangeDetectionStrategy.OnPush,

显然,使用OnPush更改检测,在单元测试中只能调用fixture.detectChanges()一次。在第一次调用之后,无论您调用fixture.detectChanges()多少次,视图都不会更新。
本文对此进行了解释:https://betterprogramming.pub/how-to-write-tests-for-components-with-onpush-change-detection-in-angular-24f2637a0f40
您可能需要一个帐户来阅读这篇文章,但其要点是创建一个新函数:

/**
 * Changes in components using OnPush strategy are only applied once when calling .detectChanges(),
 * This function solves this issue.
 */
export async function runOnPushChangeDetection(fixture: ComponentFixture<any>): Promise<void> {
  const changeDetectorRef = fixture.debugElement.injector.get<ChangeDetectorRef>(ChangeDetectorRef);
  changeDetectorRef.detectChanges();
  return fixture.whenStable();
}

在您的测试中:

it('should correctly render default state', async () => {
    expect(component.viewInitialized).toBe(true); // PASS
    await runOnPushChangeDetection(fixture);
    expect(debugElement.query(By.css('.button'))).not.toBeNull(); // FAIL
  });

也可以用fakeAsync/tick的方式使用:

it('should correctly render default state', fakeAsync(() => {
    expect(component.viewInitialized).toBe(true); // PASS
    runOnPushChangeDetection(fixture);
    tick();
    expect(debugElement.query(By.css('.button'))).not.toBeNull(); // FAIL
  }));

相关问题