Angular 中没有 TestBed 和组件的单元测试指令

Unit Tesing directives in Angular without TestBed & Component

我确实为一个指令编写了一个单元测试并且它有效但我想问我是否可以在没有组件和 TestBed 的情况下测试它。 我在这里可以改变什么或可以改进什么? 创建一个TestComponent的思路和TestBed的思路是什么

这是指令的代码。

@Directive({
  selector: '[ngModel][endOfSeasonValidation]',
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => EndOfSeasonValidationDirective),
      multi: true,
    },
  ],
})
export class EndOfSeasonValidationDirective extends AbstractValidatorDirective {
  @Input('beginDate')
  public beginDate: Date | null;

  @Input('startOfSeason')
  public startOfSeason: number | null;

  public validate(control: AbstractControl): ValidationErrors | null {
    const endOfSeason = control.value;

    if (!endOfSeason || !this.startOfSeason || !this.beginDate) {
      return null;
    }

    if (
      endOfSeason === this.startOfSeason ||
      endOfSeason + (endOfSeason < this.startOfSeason ? 12 : 0) - this.startOfSeason === 11
    ) {
      return { isInvalid: true };
    }

    return null;
  }
}

这里是单元测试的代码

@Component({
  template: `
    <form>
      <select
        endOfSeasonValidation
        [ngModel]="endOfSeason"
        name="endOfSeason"
        [beginDate]="beginDate"
        [startOfSeason]="startOfSeason"
      ></select>
    </form>
  `,
})
class EndOfSeasonValidationTestComponent {
  public beginDate = new Date('2020-01-16');
  public startOfSeason: number | null;
  public endOfSeason: number | null;
  @ViewChild(NgForm, { static: true })
  public form: NgForm;
}
describe('EndOfSeasonValidationDirective', () => {
  let fixture: ComponentFixture<EndOfSeasonValidationTestComponent>;
  let form: NgForm;
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [
        EndOfSeasonValidationTestComponent,
        EndOfSeasonValidationDirective,
        DateAccessorPluginDirective,
        SelectAccessorDirective,
      ],
      imports: [FormsModule],
    }).compileComponents();
    fixture = TestBed.createComponent(EndOfSeasonValidationTestComponent);
    fixture.detectChanges();
    await fixture.whenStable();
    form = fixture.componentInstance.form;
  });
  it('should not validate if beginDate or startOfSeason or endOfSeason are not given', async () => {
    fixture.componentInstance.beginDate = new Date('2020-01-01');
    fixture.detectChanges();
    await fixture.whenStable();
    expect(form.valid).toBeTruthy();
    expect(form.errors).toBeFalsy();
  });
  it('should validate if startOfSeason and endOfSeason are equal', async () => {
    fixture.componentInstance.beginDate = new Date('2020-12-02');
    fixture.componentInstance.startOfSeason = 2;
    fixture.componentInstance.endOfSeason = 2;
    fixture.detectChanges();
    await fixture.whenStable();
    expect(fixture.componentInstance.form.invalid).toBeTruthy();
  });
});

根据 angular docs 正确的测试方法 Directive - 是使用某些组件(或不使用 TestComponent)。
您问题的答案:

  1. 您可以测试您的指令,只需创建实例,直到它没有 DI 依赖项。

const dir = new EndOfSeasonValidationDirective();
... some changes with inputs
expect(dir.validate()).toEqual({isInvalid: true})

但在更真实的环境中测试它的更好方法 - 与某些组件配对使用。因此,创建 TestComponent 的想法是为您的单元测试提供更真实的环境(运行 所有 angular 内部齿轮)。

  1. TestBed 是解决 angular DI 依赖和创建测试模块的辅助工具。
  2. 我认为你的测试很好,但你可以通过将它覆盖到 100% 来改进它。应测试所有案例和 If 语句。

UPD 您完全应该涵盖这些情况:

  1. 如果未提供 endOfSeason 则不应验证

  2. 如果未提供 startOfSeason 则不应验证

  3. 如果未提供 beginDate 则不应验证

  4. 应该验证 startOfSeason 和 endOfSeason 是否相等

  5. 应该验证是否(我不知道这个表达式在商业用语中是什么意思)endOfSeason + (endOfSeason < this.startOfSeason ? 12 : 0) - this.startOfSeason = == 11

编写这些测试后,您对该指令的覆盖率将达到 100%,所有情况都将被测试。您可以 运行 它例如在 WebstormIntellij IDEA 中按下 Run with Coverage 按钮。