如何测试表单控件避免括号符号以保持类型检查和可维护性

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.

,这也可以帮助您改进测试