LHB*_*LHB 7 testing jasmine angular
我的任务是为使用Angular开发的聊天应用程序编写测试.下面是Angular模板代码片段,我目前正在为以下代码编写测试:
<div class="title-menu-container" fxLayoutAlign="center center">
<button id="save-title-button" mat-icon-button *ngIf="titleInputEdit; else settings">
<mat-icon class="secondary-text" (click)="saveTitle(titleInput.value)">check</mat-icon>
</button>
<ng-template #settings>
<button mat-icon-button [matMenuTriggerFor]="menu" [disabled]="!(isGroupConversation$ | async)">
<mat-icon class="secondary-text">settings</mat-icon>
</button>
</ng-template>
</div>
Run Code Online (Sandbox Code Playgroud)
实际上,如果组件布尔变量'titleInputEdit'为true,则显示save-title-button,否则显示设置按钮.以下是导致问题的测试用例:
it('save title button should be present', () => {
component.titleInputEdit = true;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#save-title-button')).not.toBe(null);
});
Run Code Online (Sandbox Code Playgroud)
我只是"模拟"组件变量,调用.detectChanges(),然后测试是否存在按钮.但是,测试失败并显示"预期的空值不为空".
通过各种console.log调用,我已经确认component.titleInputEdit正确设置为true但fixture.nativeElement不包含正确的按钮.
我注意到的一些事情:
如果我将'component.titleInputEdit = true'行移动到我的beforeEach中并将其删除,并从我的测试中调用detectChanges(),则测试通过.
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
component.titleInputEdit = true
fixture.detectChanges();
debugElement = fixture.debugElement;
});
it('save title button should be present', () => {
expect(fixture.nativeElement.querySelector('#save-title-button')).not.toBe(null);
});
Run Code Online (Sandbox Code Playgroud)如果我从beforeEach()中删除.detectChanges()调用,并将其保留在测试用例中,则测试通过.
我对Angular比较陌生,但我发现有类似问题的人的情况.在尝试了那些帖子中推荐的一些东西后,我仍然在摸不着头脑.更奇怪的是,我已经为其他Angular组件编写了测试,这些组件几乎完全相同,没有任何问题.
Angular文档中提供的示例也显示了非常相似的内容:
it('should display a different test title', () => {
component.title = 'Test Title';
fixture.detectChanges();
expect(h1.textContent).toContain('Test Title');
});
Run Code Online (Sandbox Code Playgroud)
小智 22
今天,Angular 在 Angular v14.1 中解决了这个问题。您必须通过方法在测试中设置组件的输入fixture.componentRef.setInput,以便组件将被标记为脏并且可以运行 detectorChanges。
LHB*_*LHB 19
事实证明这是由于在组件中使用ChangeDetectionStrategy.OnPush.使用OnPush只允许您调用.detectChanges()一次,因此后续调用将无法执行任何操作.我对Angular不够熟悉,无法完全理解为什么.
通过覆盖TestBed配置中的ChangeDetectionStrategy,我能够产生所需的行为.
TestBed.configureTestingModule({
imports: [],
declarations: [TestComponent],
providers: []
})
.overrideComponent(TestComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
Run Code Online (Sandbox Code Playgroud)
小智 12
直到今天还在坚持这个...
我个人喜欢最重要的ChangeDetectionStrategy 解决方案,因为它是 TestBed 设置中的一次性解决方案,但我知道这种侵入式解决方案并不理想。
TestBed.configureTestingModule({
imports: [],
declarations: [TestComponent],
providers: []
})
.overrideComponent(TestComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
Run Code Online (Sandbox Code Playgroud)
我已经看到“ChangeDetectorRef”解决方案被用在 Component 类本身上,并带有“changeDetector.markForCheck()”,这不是一个好方法,因为您的组件不必适应测试,但您仍然可以使用它解决方案,通过调用而不是正常的“detectChanges()”,而不弄乱实际组件,如此处所示
const cdr = debugEl.injector.get<ChangeDetectorRef>(ChangeDetectorRef as any);
cdr.detectChanges();
Run Code Online (Sandbox Code Playgroud)
最后有一个最简单的解决方案,至少在我的脑海中,但奇怪的是,我没有发现任何提及它的内容。因此,您可能已经知道您可以(或最终必须)创建一个主机组件来包装您正在测试的组件,例如,很多博客都展示了一种方法的使用@ViewChild(ComponentUnderTestComponent),如果 jasmine 则该方法将是完美的实际上可以感知到子组件中的变化,但是,正如它看起来的那样,事实并非如此,我们坚持使用正常的直观方法,即仅列出主机中的输入并将它们直接绑定到测试组件的模板中,像这样:
@Component({
template: `<component-tag [(ngModel)]="val" [someProperty]="flag"></component-tag>`
})
class HostComponent {
val: number;
flag: boolean = false;
}
Run Code Online (Sandbox Code Playgroud)
有了这个,现在你实际上可以更改 HostComponent.someProperty 的值,然后调用 detectorChanges() ,并且 jasmine 将完美地完成它应该做的事情,并用更改更新 DOM:
fixture.componentInstance.readonly = true;
fixture.detectChanges();
Run Code Online (Sandbox Code Playgroud)
现在,如果您的组件继续运行并具有数十个输入属性,那么我想这并不是真正可行,但无论如何,我想我会把它扔在那里,享受
就我而言,由于异步加载,我需要使用fixture.whenStable而不仅仅是fixture.detectChanges,例如
it('test description', async(async () => {
await fixture.whenStable();
}));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6783 次 |
| 最近记录: |