Angular 8 个单元测试指令,其中包括 setTimeout

Angular 8 unit test directive which includes setTimeout

我有以下简单的自动对焦指令:

@Directive({
  selector: '[appAutoFocus]',
})
export class AutofocusDirective implements AfterContentInit {

  @Input() public appAutoFocus: boolean;

  public constructor(private el: ElementRef) { }

  public ngAfterContentInit() {
    if (this.appAutoFocus) {
      setTimeout(() => {
        this.el.nativeElement.focus();
      }, 300);
    }
  }
}

我现在正在尝试编写一些简单的单元测试,但是 3 个测试中有 2 个失败了。

@Component({
  template: '<input type="text" [appAutoFocus]="true" />'
})
class TestComponent {
  constructor() { }
}

fdescribe('AutoFocusDirective', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;
  let inputEl: DebugElement;
     beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [
            TestComponent,
            AutofocusDirective
          ]
        });

        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
        inputEl = fixture.debugElement.query(By.css('input'));

        spyOn(inputEl.nativeElement, 'focus');
        fixture.detectChanges();
      });

      it('should create an instance', () => {
        expect(component).toBeTruthy();
      });

      it('should call the focus event', fakeAsync(() => {
        tick(400);
        fixture.detectChanges();
        fixture.whenStable().then(() => {
         expect(inputEl.nativeElement.focus).toHaveBeenCalled();
        });
      }));

      it('should autofocus the input control', () => {
        const debugEl: DebugElement = fixture.debugElement;
        expect(debugEl.query(By.css('input:focus'))).not.toBe(null);
      });

"Should call the focus event" 失败 Spec 'AutoFocusDirective should call the focus event' has no expectations.

"Should autofocus the input control" 失败 Expected null not to be null

也许你不能将 fakeAsyncfixture.whenStable 结合起来。

尝试:

      it('should call the focus event', fakeAsync(() => {
        tick(400);
        fixture.detectChanges();
        // You should check out `flush` method to resolve instead of specifying the time in the argument of tick
        expect(inputEl.nativeElement.focus).toHaveBeenCalled();
      }));

     it('should autofocus the input control', fakeAsync(() => {
        tick(400);
        fixture.detectChanges();
        const debugEl: DebugElement = fixture.debugElement;
        expect(debugEl.query(By.css('input:focus'))).not.toBe(null);
      }));

你的两个测试做同样的事情,所以考虑把它们放在一个 it 块中。

适合您的读物:https://alligator.io/angular/testing-async-fakeasync/

我能让它工作的唯一方法是在测试规范中添加一个 setTimeout,所以它现在看起来像这样:

  it('should call the focus event', (done) => {
    fixture.detectChanges();
    setTimeout(() => {
      expect(inputEl.nativeElement.focus).toHaveBeenCalled();
      done();
    }, 500);
  });

老实说,这是一个垃圾解决方案,但我在这上面浪费了太多时间。