如何测试表单控件避免括号符号以保持类型检查和可维护性
How to test form controls avoiding bracket notation to preserve type checking and maintainability
问题
当我重构一个对象的成员时,我对表单的单元测试中断了,因为我只能使用方括号表示法访问 fromgroup 上的表单控件。这种基于字符串的被测成员选择不会被任何 IDE.
使用代码重构
这是一个例子...
对象:
class Task {
constructor(
public id: number = 0,
public description: string = '', // <--- member under test
public completed: boolean = false,
public priority: Priority = Priority.Normal
) {}
clone(): Task {
return new Task(this.id, this.description, this.completed, this.priority);
}
}
组件:
export class AppComponent implements OnInit {
form!: FormGroup;
task: Task;
priorities = [Priority.Low, Priority.Normal, Priority.High];
constructor(private fb: FormBuilder) {
this.task = this.mockTask();
}
ngOnInit() {
this.task = this.mockTask();
this.form = this.fb.group({
description: [
this.task.description,
Validators.compose([Validators.required, Validators.maxLength(50)]),
],
priority: [this.task.priority],
});
}
// ...
}
测试:
describe('AppComponent', () => {
let comp: AppComponent
let fixture: ComponentFixture<AppComponent>;
let task: Task;
let taskService: TaskService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [AppComponent],
providers: [{provide: TaskService, useClass: MockTaskService}]
}).compileComponents()
}))
beforeEach((done) => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
taskService = fixture.debugElement.injector.get(TaskService);
taskService.getAllTasks().subscribe(tasks => {
task = tasks[0];
comp.task = task;
comp.ngOnInit();
fixture.detectChanges();
done()
})
})
it('should not update value if form is invalid', () => {
let spy = spyOn(taskService, 'updateTask')
comp.form.controls['description'].setValue(null) // <------
comp.onSubmit()
expect(comp.form.valid).toBeFalsy();
expect(spy).not.toHaveBeenCalled();
})
})
注意箭头旁边的括号符号。我希望能够以某种方式使用它:
comp.form.controls.description.setValue(null)
我尝试过的
我试过创建一些界面,例如:
interface ITask {
description: string;
}
interface ITaskFormGroup extends FormGroup {
value: ITask;
controls: {
description: AbstractControl;
};
}
问题在于控件是 AbstractControl 类型,而所讨论的 Task 成员是字符串类型。所以 'controls' 不能将 ITask 作为通用接口来实现。
另外,这个:
"compilerOptions": {
"noPropertyAccessFromIndexSignature": false,
// ...
}
... 允许使用点符号,但是当我在 Task 或 ITask 中更改成员 'description' 时,这仍然不会重构测试。
我觉得这个问题应该有一些解决方案,但我需要一些帮助才能解决这个问题。我想知道这需要付出多少努力,这样我才能看看在长期 运行.
中是否值得
完整示例
尽管我认为您不能在 Stackblitz 上使用测试 运行ner,但我提供了一个更完整的示例。解决语法问题运行宁测试应该不需要。
从 FormGroup 获取表单控件的正确方法是使用其 get
函数:
const descControl = comp.form.get('description')
descControl.setValue(null)
如果表单控件丢失,至少您的测试会引发错误。
angular 团队已实施 strong typed forms。如果您有耐心等待 angular 14.
,这也可以帮助您改进测试
问题
当我重构一个对象的成员时,我对表单的单元测试中断了,因为我只能使用方括号表示法访问 fromgroup 上的表单控件。这种基于字符串的被测成员选择不会被任何 IDE.
使用代码重构这是一个例子...
对象:
class Task {
constructor(
public id: number = 0,
public description: string = '', // <--- member under test
public completed: boolean = false,
public priority: Priority = Priority.Normal
) {}
clone(): Task {
return new Task(this.id, this.description, this.completed, this.priority);
}
}
组件:
export class AppComponent implements OnInit {
form!: FormGroup;
task: Task;
priorities = [Priority.Low, Priority.Normal, Priority.High];
constructor(private fb: FormBuilder) {
this.task = this.mockTask();
}
ngOnInit() {
this.task = this.mockTask();
this.form = this.fb.group({
description: [
this.task.description,
Validators.compose([Validators.required, Validators.maxLength(50)]),
],
priority: [this.task.priority],
});
}
// ...
}
测试:
describe('AppComponent', () => {
let comp: AppComponent
let fixture: ComponentFixture<AppComponent>;
let task: Task;
let taskService: TaskService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [AppComponent],
providers: [{provide: TaskService, useClass: MockTaskService}]
}).compileComponents()
}))
beforeEach((done) => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
taskService = fixture.debugElement.injector.get(TaskService);
taskService.getAllTasks().subscribe(tasks => {
task = tasks[0];
comp.task = task;
comp.ngOnInit();
fixture.detectChanges();
done()
})
})
it('should not update value if form is invalid', () => {
let spy = spyOn(taskService, 'updateTask')
comp.form.controls['description'].setValue(null) // <------
comp.onSubmit()
expect(comp.form.valid).toBeFalsy();
expect(spy).not.toHaveBeenCalled();
})
})
注意箭头旁边的括号符号。我希望能够以某种方式使用它:
comp.form.controls.description.setValue(null)
我尝试过的
我试过创建一些界面,例如:
interface ITask {
description: string;
}
interface ITaskFormGroup extends FormGroup {
value: ITask;
controls: {
description: AbstractControl;
};
}
问题在于控件是 AbstractControl 类型,而所讨论的 Task 成员是字符串类型。所以 'controls' 不能将 ITask 作为通用接口来实现。
另外,这个:
"compilerOptions": {
"noPropertyAccessFromIndexSignature": false,
// ...
}
... 允许使用点符号,但是当我在 Task 或 ITask 中更改成员 'description' 时,这仍然不会重构测试。
我觉得这个问题应该有一些解决方案,但我需要一些帮助才能解决这个问题。我想知道这需要付出多少努力,这样我才能看看在长期 运行.
中是否值得完整示例
尽管我认为您不能在 Stackblitz 上使用测试 运行ner,但我提供了一个更完整的示例。解决语法问题运行宁测试应该不需要。
从 FormGroup 获取表单控件的正确方法是使用其 get
函数:
const descControl = comp.form.get('description')
descControl.setValue(null)
如果表单控件丢失,至少您的测试会引发错误。
angular 团队已实施 strong typed forms。如果您有耐心等待 angular 14.
,这也可以帮助您改进测试