Angular Karma Jasmine 测试表单验证错误
Angular Karma Jasmine Testing form validation error
我想进行测试以确保我在构建时给予表单控件的验证得到应用。这可能是因为我在检查错误之前设置了表单控件值吗?该表单是在专用于整体表单的服务中构建的。这是一份 6 页的登记表。我想确保在我的 karma-jasmine 测试中应用了我用来构建表单的验证器。申请人在 onInit 中定义了一个对 back-end 进行 http 调用的服务和另一个构建表单的服务。我已经创建了一个通用的注册表格模拟来表示从规范中的 back-end 调用返回的数据。理想情况下,我需要开始工作的是期望所需的验证器和验证器模式实际上是在构建时传递到表单中的内容:
(请参阅此 post 底部的错误照片)
expect(errors.required).toBeTruthy();
expect(errors.pattern).toBeTruthy();
此表单控件在构建时被传递 Validators.required。但是,在测试期间,验证器错误显示为未定义 ...
表单生成器:
phone_number: [null, Validators.required],
表单验证规范:
it('phone number field validity - required', () => {
const phoneNumber = component.applicant.controls.phone_number;
expect(phoneNumber.valid).toBeFalsy();
phoneNumber.setValue(mockGenericMaEnrollmentRefresh.applicant.phone_number);
const errors = phoneNumber.errors || {};
// expect(errors.required).toBeTruthy();
expect(errors.pattern).toBe(undefined);
expect(phoneNumber.valid).toBeTruthy();
});
分量HTML:
<mat-form-field>
<mat-label>Phone *</mat-label>
<input [textMask]="{mask: phoneMask}" formControlName="phone_number" id="phone_number" matInput
type="text">
<mat-error *ngIf="applicant.controls.phone_number.errors?.required">Phone Number is
required.
</mat-error>
</mat-form-field>
组件 TS:
import {Component, OnInit} from '@angular/core';
import {GoToStepConfig, MaNavigationService} from '../../services/ma-navigation/ma-navigation.service';
import {MaEnrollmentFormService} from '../../services/ma-enrollment-form/ma-enrollment-form.service';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'app-ma-personal-info',
templateUrl: './ma-personal-info.component.html',
styleUrls: ['./ma-personal-info.component.css']
})
export class MaPersonalInfoComponent implements OnInit {
phoneMask: any[] = [/\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/];
mailingAddressSameAsResidential = new FormControl(null);
constructor(private maNavigationService: MaNavigationService,
private formService: MaEnrollmentFormService) {
}
get enrollmentForm(): FormGroup {
return this.formService.enrollmentForm;
}
get applicant(): FormGroup {
return this.formService.applicant as FormGroup;
}
get permanentAddress(): FormGroup {
return this.formService.permanentAddress as FormGroup;
}
get mailingAddress(): FormGroup {
return this.formService.mailingAddress as FormGroup;
}
get medicaid(): FormGroup {
return this.formService.medicaid as FormGroup;
}
get providerForm(): FormGroup {
return this.formService.providerForm as FormGroup;
}
ngOnInit() {
}
isApplicantBasicInfoValid(): boolean {
return this.applicant.controls.first_name.valid &&
this.applicant.controls.last_name.valid &&
this.applicant.controls.date_of_birth.valid &&
this.applicant.controls.phone_number.valid &&
this.applicant.controls.gender.valid;
}
isAddressValid(): boolean {
return this.permanentAddress.valid && this.mailingAddress.valid;
}
isMedicareInformationValid(): boolean {
return this.applicant.controls.medicare_claim_number.valid &&
this.applicant.controls.hospital_insurance_parta.valid &&
this.applicant.controls.medical_insurance_partb.valid &&
this.enrollmentForm.controls.proposed_effective_date.valid;
}
isMedicaidInformationValid(): boolean {
return this.providerForm.valid && this.medicaid.valid;
}
areAllControlsValid(): boolean {
return this.applicant.valid && this.medicaid.valid && this.providerForm.valid &&
this.enrollmentForm.controls.proposed_effective_date.valid;
}
back() {
this.maNavigationService.returnToQuotePage();
}
next() {
this.applicant.markAllAsTouched();
this.medicaid.markAllAsTouched();
this.permanentAddress.markAllAsTouched();
this.mailingAddress.markAllAsTouched();
this.providerForm.markAllAsTouched();
if (!this.areAllControlsValid()) {
return;
}
const goToStepConfig: GoToStepConfig = {
route: '/ma/enroll/2'
};
this.maNavigationService.goToStep(goToStepConfig);
}
}
///////////// PAGE 1 GETTERS FORM SERVICE ///////////////
get applicant(): FormGroup {
return this.enrollmentForm.get('applicant') as FormGroup;
}
get permanentAddress(): FormGroup {
return this.applicant.get('permanent_address') as FormGroup;
}
get mailingAddress(): FormGroup {
return this.applicant.get('mailing_address') as FormGroup;
}
get medicaid(): FormGroup {
return this.enrollmentForm.get('medicaid') as FormGroup;
}
get providerForm(): FormGroup {
return this.enrollmentForm.get('plan.primary_care_physician.provider') as FormGroup;
}
这个问题似乎没有发生某些变化检测。当您直接更新内部值时,表单可能会有点奇怪。每当表单检测到发生更改时,验证程序逻辑都会重新 运行,这通常发生在直接使用字段时(想想 blur/dirty)。但是,您设置值的方式可能会绕过这一点。
一种确保验证逻辑 运行 并且您得到适当的错误 response/form 逻辑的方法是告诉表单控件它已过时。
component.yourForm.controls['yourFormField'].markAsTouched();
fixture.detectChanges();
这将 运行 验证器,您将看到预期的行为。
请注意,您可能还想尝试使用本机元素来更新表单,而不是转到组件本身。这确保测试检查有助于模拟用户体验的模板。如果您选择走那条路线,您可以使用此代码。
yourFormInput = dom.query(By.css('[formcontrolname=yourFormField]')).nativeElement;
yourFormInput.value = deviceName;
yourFormInput.dispatchEvent(new Event('input'));
fixture.detectChanges();
对我有用的方法是调用 markAsTouched()
函数并手动设置该控件的错误。
component.yourForm.controls['yourFormField'].markAsTouched();
component.yourForm.controls['yourFormField'].setErrors({ required: true });
fixture.detectChanges();
希望回答对其他人有所帮助。
从 Angular 材料的版本 9 开始,ComponentHarnesses 与测试 API 一起提供。
为了测试垫输入错误,你可以尝试 MatInputHarness
.
it( 'should display validation errors when the input is focus and the value is invalid', async () => {
const matFormFieldHarness = await loader.getHarness( MatFormFieldHarness );
const inputHarness = await matFormFieldHarness .getHarness( MatInputHarness );
await inputHarness.focus(); // focus in input
await inputHarness.setValue( invalid_value ); // set value to input
await inputHarness.blur(); // focus out input
const errors = await matFormFieldHarness.getTextErrors(); // get an array of
validation errors
expect( errors ).toContain( error_message );
} );
我想进行测试以确保我在构建时给予表单控件的验证得到应用。这可能是因为我在检查错误之前设置了表单控件值吗?该表单是在专用于整体表单的服务中构建的。这是一份 6 页的登记表。我想确保在我的 karma-jasmine 测试中应用了我用来构建表单的验证器。申请人在 onInit 中定义了一个对 back-end 进行 http 调用的服务和另一个构建表单的服务。我已经创建了一个通用的注册表格模拟来表示从规范中的 back-end 调用返回的数据。理想情况下,我需要开始工作的是期望所需的验证器和验证器模式实际上是在构建时传递到表单中的内容:
(请参阅此 post 底部的错误照片)
expect(errors.required).toBeTruthy();
expect(errors.pattern).toBeTruthy();
此表单控件在构建时被传递 Validators.required。但是,在测试期间,验证器错误显示为未定义 ...
表单生成器:
phone_number: [null, Validators.required],
表单验证规范:
it('phone number field validity - required', () => {
const phoneNumber = component.applicant.controls.phone_number;
expect(phoneNumber.valid).toBeFalsy();
phoneNumber.setValue(mockGenericMaEnrollmentRefresh.applicant.phone_number);
const errors = phoneNumber.errors || {};
// expect(errors.required).toBeTruthy();
expect(errors.pattern).toBe(undefined);
expect(phoneNumber.valid).toBeTruthy();
});
分量HTML:
<mat-form-field>
<mat-label>Phone *</mat-label>
<input [textMask]="{mask: phoneMask}" formControlName="phone_number" id="phone_number" matInput
type="text">
<mat-error *ngIf="applicant.controls.phone_number.errors?.required">Phone Number is
required.
</mat-error>
</mat-form-field>
组件 TS:
import {Component, OnInit} from '@angular/core';
import {GoToStepConfig, MaNavigationService} from '../../services/ma-navigation/ma-navigation.service';
import {MaEnrollmentFormService} from '../../services/ma-enrollment-form/ma-enrollment-form.service';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'app-ma-personal-info',
templateUrl: './ma-personal-info.component.html',
styleUrls: ['./ma-personal-info.component.css']
})
export class MaPersonalInfoComponent implements OnInit {
phoneMask: any[] = [/\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/];
mailingAddressSameAsResidential = new FormControl(null);
constructor(private maNavigationService: MaNavigationService,
private formService: MaEnrollmentFormService) {
}
get enrollmentForm(): FormGroup {
return this.formService.enrollmentForm;
}
get applicant(): FormGroup {
return this.formService.applicant as FormGroup;
}
get permanentAddress(): FormGroup {
return this.formService.permanentAddress as FormGroup;
}
get mailingAddress(): FormGroup {
return this.formService.mailingAddress as FormGroup;
}
get medicaid(): FormGroup {
return this.formService.medicaid as FormGroup;
}
get providerForm(): FormGroup {
return this.formService.providerForm as FormGroup;
}
ngOnInit() {
}
isApplicantBasicInfoValid(): boolean {
return this.applicant.controls.first_name.valid &&
this.applicant.controls.last_name.valid &&
this.applicant.controls.date_of_birth.valid &&
this.applicant.controls.phone_number.valid &&
this.applicant.controls.gender.valid;
}
isAddressValid(): boolean {
return this.permanentAddress.valid && this.mailingAddress.valid;
}
isMedicareInformationValid(): boolean {
return this.applicant.controls.medicare_claim_number.valid &&
this.applicant.controls.hospital_insurance_parta.valid &&
this.applicant.controls.medical_insurance_partb.valid &&
this.enrollmentForm.controls.proposed_effective_date.valid;
}
isMedicaidInformationValid(): boolean {
return this.providerForm.valid && this.medicaid.valid;
}
areAllControlsValid(): boolean {
return this.applicant.valid && this.medicaid.valid && this.providerForm.valid &&
this.enrollmentForm.controls.proposed_effective_date.valid;
}
back() {
this.maNavigationService.returnToQuotePage();
}
next() {
this.applicant.markAllAsTouched();
this.medicaid.markAllAsTouched();
this.permanentAddress.markAllAsTouched();
this.mailingAddress.markAllAsTouched();
this.providerForm.markAllAsTouched();
if (!this.areAllControlsValid()) {
return;
}
const goToStepConfig: GoToStepConfig = {
route: '/ma/enroll/2'
};
this.maNavigationService.goToStep(goToStepConfig);
}
}
///////////// PAGE 1 GETTERS FORM SERVICE ///////////////
get applicant(): FormGroup {
return this.enrollmentForm.get('applicant') as FormGroup;
}
get permanentAddress(): FormGroup {
return this.applicant.get('permanent_address') as FormGroup;
}
get mailingAddress(): FormGroup {
return this.applicant.get('mailing_address') as FormGroup;
}
get medicaid(): FormGroup {
return this.enrollmentForm.get('medicaid') as FormGroup;
}
get providerForm(): FormGroup {
return this.enrollmentForm.get('plan.primary_care_physician.provider') as FormGroup;
}
这个问题似乎没有发生某些变化检测。当您直接更新内部值时,表单可能会有点奇怪。每当表单检测到发生更改时,验证程序逻辑都会重新 运行,这通常发生在直接使用字段时(想想 blur/dirty)。但是,您设置值的方式可能会绕过这一点。
一种确保验证逻辑 运行 并且您得到适当的错误 response/form 逻辑的方法是告诉表单控件它已过时。
component.yourForm.controls['yourFormField'].markAsTouched();
fixture.detectChanges();
这将 运行 验证器,您将看到预期的行为。
请注意,您可能还想尝试使用本机元素来更新表单,而不是转到组件本身。这确保测试检查有助于模拟用户体验的模板。如果您选择走那条路线,您可以使用此代码。
yourFormInput = dom.query(By.css('[formcontrolname=yourFormField]')).nativeElement;
yourFormInput.value = deviceName;
yourFormInput.dispatchEvent(new Event('input'));
fixture.detectChanges();
对我有用的方法是调用 markAsTouched()
函数并手动设置该控件的错误。
component.yourForm.controls['yourFormField'].markAsTouched();
component.yourForm.controls['yourFormField'].setErrors({ required: true });
fixture.detectChanges();
希望回答对其他人有所帮助。
从 Angular 材料的版本 9 开始,ComponentHarnesses 与测试 API 一起提供。
为了测试垫输入错误,你可以尝试 MatInputHarness
.
it( 'should display validation errors when the input is focus and the value is invalid', async () => {
const matFormFieldHarness = await loader.getHarness( MatFormFieldHarness );
const inputHarness = await matFormFieldHarness .getHarness( MatInputHarness );
await inputHarness.focus(); // focus in input
await inputHarness.setValue( invalid_value ); // set value to input
await inputHarness.blur(); // focus out input
const errors = await matFormFieldHarness.getTextErrors(); // get an array of
validation errors
expect( errors ).toContain( error_message );
} );