我有一个演示/哑组件,它使用输入来使用 formBuilder 填充 HTML 模板。这一切都很好,但是当我开始测试它时,事情并不像他们应该的那样顺利。
我想测试一些文本框是否正确填充了我的输入值。我希望 formGroup 在我之后更新我的 HTML:
- 设置输入
- 呼叫检测变化
- 调用 ngOnInit
在我的测试中,我使用默认行为覆盖 OnPush 检测策略。
最终我的目标是使用 Jest 对这类组件进行快照测试,因为它使比较属性变得更加容易,但要做到这一点,我首先需要能够正确呈现我的 HTML。
我正在使用 Jest 运行测试,我相信它使用 JsDOM。
@Component({
selector: 'network',
templateUrl: './network.component.html',
styleUrls: ['./network.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NetworkComponent implements OnInit {
@Input() network: Network;
public formGroup: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.formGroup = this.formBuilder.group({
details: [this.network || null],
});
}
}
<div class="container-fluid">
<form [formGroup]="formGroup" novalidate (ngSubmit)="submit()">
<div class="row">
<div class="col-4">
<h2>Details</h2>
<app-network-details formControlName="details"></app-network-details>
</div>
</div>
<div class="row">
<button class="btn btn-primary" type="submit">{{submitButtonText}}</button>
</div>
</form>
</div>
我的 app-network-details 组件如下所示:
@Component({
selector: 'app-network-details',
templateUrl: './network-details.component.html',
styleUrls: ['./network-details.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NetworkDetailsComponent),
multi: true
}
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NetworkDetailsComponent implements OnInit, ControlValueAccessor, OnChanges {
public details = {};
@Output() change = new EventEmitter<number>();
propagateChange = (_: any) => {};
ngOnChanges(changes: SimpleChanges): void {
this.propagateChange(this.details);
}
writeValue(obj: any): void {
if (obj) {
this.details = obj;
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {}
setDisabledState?(isDisabled: boolean): void {
// throw new Error('Method not implemented.');
}
public onChange(e: any) {
this.details[e.target.name] = e.target.value;
this.propagateChange(this.details);
}
constructor() {
this.details = {};
}
ngOnInit() {}
}
<div class="form-group">
<input class="form-control" type="text" placeholder="Name" name="name" (change)="onChange($event)" [value]="details.name || null"
/>
</div>
<div class="form-group">
<input class="form-control" type="text" placeholder="Description" name="description" (change)="onChange($event)" [value]="details.description || null"
/>
</div>
<div class="form-group">
<input class="form-control" type="text" placeholder="Local Code" name="localCode" (change)="onChange($event)" [value]="details.localCode || null"
/>
</div>
这是我的测试设置:
describe('NetworkComponent', () => {
let component: NetworkComponent;
let fixture: ComponentFixture<NetworkComponent>;
const compilerConfig = { preserveWhitespaces: false } as any;
beforeEach(
async(() => {
TestBed.configureCompiler(compilerConfig)
.configureTestingModule({
declarations: [
NetworkComponent,
NetworkDetailsComponent,
NetworkAuthorisationComponent,
MailboxListStubComponent,
AddMailboxStubComponent,
UpdateMailboxStubComponent,
DropdownStubComponent
],
imports: [ReactiveFormsModule],
providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }]
})
.overrideComponent(NetworkComponent, {
set: {
changeDetection: ChangeDetectionStrategy.Default
}
})
.compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(NetworkComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should intialize inputs with values from network', () => {
const network: Network = {
id: 1,
name: 'a',
description: 'b',
localCode: 'c',
authorisation: 'd',
company: 'e',
networkType: 'oftp'
};
component.network = network;
fixture.detectChanges();
component.ngOnInit();
const inputElement = fixture.nativeElement.querySelector('input[name="description"]');
inputElement.dispatchEvent(new Event('change'));
fixture.detectChanges();
expect(inputElement.value).toEqual('b');
});
});
编辑: 我已经设法隔离了问题。由于我的 NetworkDetailsComponent,我的测试失败了。我可能没有正确实现 ControlValueAccessor 接口。如果我重写我的测试并在具有 formControlName 指令的输入元素上断言,它可以工作。