使用 Jest 测试 Angular 表单 - 填写和查询表单字段
Testing an Angular Form with Jest - fill and query form fields
如何在 Jest 测试脚本中查询和填写表单字段?我的工作环境是Angular13.
In 看到了类似的帖子并受到启发使用替代答案。但我不断收到属性不存在的错误。
我的简化形式是:
<form name="frmProduct">
<div class="form-group row">
<label class="col-2" for="ProductPrice">Product price</label>
<input class="col-8 form-control" id="ProductPrice" name="ProductPrice" [(ngModel)]="productPrice" type="text">
</div>
<div class="form-group row">
<input class="col-2 btn btn-warning pl-1" (click)="clear()" type="button" value="Clear">
<input class="col-2 btn btn-success pl-2" (click)="save()" type="button" value="Save">
</div>
</form>
Try-1:我的 Jest 测试代码是...有 属性 不存在的错误。
describe('ProductFormComponent', () => {
let component: ProductcomponentComponent;
let fixture: ComponentFixture<ProductcomponentComponent>;
let button: HTMLElement;
beforeAll(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting());
});
beforeEach( () => {
TestBed.configureTestingModule({
declarations: [ProductcomponentComponent],
imports: [FormsModule, ReactiveFormsModule ]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProductcomponentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
// Below is the synchronous way. I also tried the async way via fakeAsync( .. )
it('should calculate tax based on base price when save button is clicked', () => {
productPrice = 4000;
fixture.detectChanges();
expect( getFormElementValue( fixture.nativeElement, '#ProductPrice')).toEqual( '4000')
});
function getFormElementValue( componentForm: any, selector:string): string {
return componentForm.querySelector( selector).value;
}
});
编辑:在“@rfprod”的帮助下解决了这个问题。非常感谢!
组件设置,表单域读取:
it('should calculate tax based on base price when save button is clicked', async () => {
component.productPrice = 4000;
fixture.detectChanges();
fixture.whenStable().then( () => {
expect(getFormElementValue(fixture.nativeElement, '#ProductPrice')).toEqual('4000')
});
});
function getFormElementValue( componentForm: any, selector:string): string {
return componentForm.querySelector( selector).value;
}
设置和读取表单域:
it('should calculate tax based on base price when save button is clicked', async () => {
component.product.BasePrice = 4000;
fixture.detectChanges();
fixture.whenStable().then( () => {
setFormElementValue(fixture.nativeElement, '#BasePrice', 5000)
});
fixture.whenStable().then( () => {
expect(getFormElementValue(fixture.nativeElement, '#BasePrice')).toEqual('5000')
});
});
function setFormElementValue(componentForm: any, selector: string, value: number) {
let element = componentForm.querySelector( selector);
element.value = value;
element.dispatchEvent(new Event('input'));
fixture.detectChanges();
}
似乎应该
it('should calculate tax based on base price when save button is clicked', () => {
component.productPrice = 4000;
fixture.detectChanges();
expect( getFormElementValue( fixture.nativeElement, '#ProductPrice')).toEqual( '4000')
});
我建议使用反应形式而不是 [(ngModel)]
https://angular.io/api/forms/ReactiveFormsModule
你的测试有一些问题。第一,你的笑话测试似乎不完整。例如,您需要在 组件 属性.
上设置产品价格
第二,当您获得价格时,您需要更好地定位选择器。
这里有几个对我有用的例子:
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { EventEmitter, Output } from '@angular/core';
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture<FormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FormComponent ],
imports: [FormsModule],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set the price in the form, when we set it in the model', () => {
component.productPrice = '4000';
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('');
fixture.whenStable()
.then(() => {
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
});
it('should do this with "waitForAsync"', waitForAsync(() => {
component.productPrice = '4000';
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('');
fixture.whenStable()
.then(() => {
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
}));
// maybe you wanna create an output and listen for changes?
// like, save will output productPrice + 20%
// your component has:
// @Output() priceWithTax = new EventEmitter<string>();
// and the save method:
// this.priceWithTax.emit( <product price increased by 20 %> )
it('should do this with "waitForAsync"', waitForAsync((done: any) => {
component.priceWithTax
.subscribe((value: number) => {
expect(value).toEqual(4800);
done();
})
component.productPrice = '4000';
fixture.detectChanges();
}));
});
我的组件模板与您的相似:
<form name="frmProduct">
<div class="form-group row">
<label class="col-2" for="ProductPrice">Product price</label>
<input class="col-8 form-control" id="ProductPrice" name="ProductPrice" [(ngModel)]="productPrice" type="text">
</div>
<div class="form-group row">
<input class="col-2 btn btn-warning pl-1" (click)="clear()" type="button" value="Clear">
<input class="col-2 btn btn-success pl-2" (click)="save()" type="button" value="Save">
</div>
</form>
而我的组件class是这样的:
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'ng-jest-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
@Output() priceWithTax = new EventEmitter<number>();
productPrice = '';
constructor() { }
ngOnInit(): void {
console.log('initialized')
}
clear() {
this.productPrice = '';
}
save() {
console.log('PP:', this.productPrice);
const price = parseInt(this.productPrice, 10);
const value = Math.round(price * 1.2)
this.priceWithTax.emit(value)
}
}
这些是我得到的结果:
编辑:这是一个更新的同步版本
所以,为了完整起见,这里有一个更新版本,带有 ReactiveFormsModule。响应式表单 是同步的 ,虽然它们在一个地方添加了一些样板,但您可以在其他许多地方保存它。
所以我的模板会有
<input [formControl]="productPriceControl">
而不是
<input [(ngModel)]="productPrice">
我的组件将具有:
class FormComponent {
productPriceControl = new FormControl('');
...
// here's how to read the value from the control
save() {
const currentPrice = this.productPriceControl.value;
// calculate price with tax
}
// here's how to write the value
clear() {
this.productPriceControl.setValue('');
}
...
}
现在,我的测试是这样的:
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { EventEmitter, Output } from '@angular/core';
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture<FormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FormComponent ],
imports: [ ReactiveFormsModule ],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set the price in the form, when we set it in the model', () => {
component.productPriceControl.setValue('4000');
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
it('should set the price in the control, when we set it in the template', () => {
const input = fixture.nativeElement.querySelector('#ProductPrice');
input.value = '4000';
input.dispatchEvent(new Event('input'));
expect(component.productPriceControl.value).toEqual('4000')
});
});
如您所见,没有异步代码,没有waitForAsync
或任何东西。一切都立即同步。
希望对以后的工作有所帮助。
如何在 Jest 测试脚本中查询和填写表单字段?我的工作环境是Angular13.
In 看到了类似的帖子并受到启发使用替代答案。但我不断收到属性不存在的错误。
我的简化形式是:
<form name="frmProduct">
<div class="form-group row">
<label class="col-2" for="ProductPrice">Product price</label>
<input class="col-8 form-control" id="ProductPrice" name="ProductPrice" [(ngModel)]="productPrice" type="text">
</div>
<div class="form-group row">
<input class="col-2 btn btn-warning pl-1" (click)="clear()" type="button" value="Clear">
<input class="col-2 btn btn-success pl-2" (click)="save()" type="button" value="Save">
</div>
</form>
Try-1:我的 Jest 测试代码是...有 属性 不存在的错误。
describe('ProductFormComponent', () => {
let component: ProductcomponentComponent;
let fixture: ComponentFixture<ProductcomponentComponent>;
let button: HTMLElement;
beforeAll(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting());
});
beforeEach( () => {
TestBed.configureTestingModule({
declarations: [ProductcomponentComponent],
imports: [FormsModule, ReactiveFormsModule ]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProductcomponentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
// Below is the synchronous way. I also tried the async way via fakeAsync( .. )
it('should calculate tax based on base price when save button is clicked', () => {
productPrice = 4000;
fixture.detectChanges();
expect( getFormElementValue( fixture.nativeElement, '#ProductPrice')).toEqual( '4000')
});
function getFormElementValue( componentForm: any, selector:string): string {
return componentForm.querySelector( selector).value;
}
});
编辑:在“@rfprod”的帮助下解决了这个问题。非常感谢!
组件设置,表单域读取:
it('should calculate tax based on base price when save button is clicked', async () => {
component.productPrice = 4000;
fixture.detectChanges();
fixture.whenStable().then( () => {
expect(getFormElementValue(fixture.nativeElement, '#ProductPrice')).toEqual('4000')
});
});
function getFormElementValue( componentForm: any, selector:string): string {
return componentForm.querySelector( selector).value;
}
设置和读取表单域:
it('should calculate tax based on base price when save button is clicked', async () => {
component.product.BasePrice = 4000;
fixture.detectChanges();
fixture.whenStable().then( () => {
setFormElementValue(fixture.nativeElement, '#BasePrice', 5000)
});
fixture.whenStable().then( () => {
expect(getFormElementValue(fixture.nativeElement, '#BasePrice')).toEqual('5000')
});
});
function setFormElementValue(componentForm: any, selector: string, value: number) {
let element = componentForm.querySelector( selector);
element.value = value;
element.dispatchEvent(new Event('input'));
fixture.detectChanges();
}
似乎应该
it('should calculate tax based on base price when save button is clicked', () => {
component.productPrice = 4000;
fixture.detectChanges();
expect( getFormElementValue( fixture.nativeElement, '#ProductPrice')).toEqual( '4000')
});
我建议使用反应形式而不是 [(ngModel)]
https://angular.io/api/forms/ReactiveFormsModule
你的测试有一些问题。第一,你的笑话测试似乎不完整。例如,您需要在 组件 属性.
上设置产品价格第二,当您获得价格时,您需要更好地定位选择器。
这里有几个对我有用的例子:
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { EventEmitter, Output } from '@angular/core';
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture<FormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FormComponent ],
imports: [FormsModule],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set the price in the form, when we set it in the model', () => {
component.productPrice = '4000';
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('');
fixture.whenStable()
.then(() => {
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
});
it('should do this with "waitForAsync"', waitForAsync(() => {
component.productPrice = '4000';
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('');
fixture.whenStable()
.then(() => {
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
}));
// maybe you wanna create an output and listen for changes?
// like, save will output productPrice + 20%
// your component has:
// @Output() priceWithTax = new EventEmitter<string>();
// and the save method:
// this.priceWithTax.emit( <product price increased by 20 %> )
it('should do this with "waitForAsync"', waitForAsync((done: any) => {
component.priceWithTax
.subscribe((value: number) => {
expect(value).toEqual(4800);
done();
})
component.productPrice = '4000';
fixture.detectChanges();
}));
});
我的组件模板与您的相似:
<form name="frmProduct">
<div class="form-group row">
<label class="col-2" for="ProductPrice">Product price</label>
<input class="col-8 form-control" id="ProductPrice" name="ProductPrice" [(ngModel)]="productPrice" type="text">
</div>
<div class="form-group row">
<input class="col-2 btn btn-warning pl-1" (click)="clear()" type="button" value="Clear">
<input class="col-2 btn btn-success pl-2" (click)="save()" type="button" value="Save">
</div>
</form>
而我的组件class是这样的:
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'ng-jest-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
@Output() priceWithTax = new EventEmitter<number>();
productPrice = '';
constructor() { }
ngOnInit(): void {
console.log('initialized')
}
clear() {
this.productPrice = '';
}
save() {
console.log('PP:', this.productPrice);
const price = parseInt(this.productPrice, 10);
const value = Math.round(price * 1.2)
this.priceWithTax.emit(value)
}
}
这些是我得到的结果:
编辑:这是一个更新的同步版本
所以,为了完整起见,这里有一个更新版本,带有 ReactiveFormsModule。响应式表单 是同步的 ,虽然它们在一个地方添加了一些样板,但您可以在其他许多地方保存它。
所以我的模板会有
<input [formControl]="productPriceControl">
而不是
<input [(ngModel)]="productPrice">
我的组件将具有:
class FormComponent {
productPriceControl = new FormControl('');
...
// here's how to read the value from the control
save() {
const currentPrice = this.productPriceControl.value;
// calculate price with tax
}
// here's how to write the value
clear() {
this.productPriceControl.setValue('');
}
...
}
现在,我的测试是这样的:
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { EventEmitter, Output } from '@angular/core';
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture<FormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FormComponent ],
imports: [ ReactiveFormsModule ],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set the price in the form, when we set it in the model', () => {
component.productPriceControl.setValue('4000');
expect(fixture.nativeElement.querySelector('#ProductPrice').value).toEqual('4000');
});
it('should set the price in the control, when we set it in the template', () => {
const input = fixture.nativeElement.querySelector('#ProductPrice');
input.value = '4000';
input.dispatchEvent(new Event('input'));
expect(component.productPriceControl.value).toEqual('4000')
});
});
如您所见,没有异步代码,没有waitForAsync
或任何东西。一切都立即同步。
希望对以后的工作有所帮助。