Angular — модульный тест-шпион не распознает, что функция была вызвана

У меня есть тестовый компонент, который я использую для тестирования директив:

export class UnitTestComponent implements OnInit {
  @ViewChild(BackgroundLoadedDirective) backgroundLoaded: BackgroundLoadedDirective;

  public url = 'https://www.codeproject.com/KB/GDI-plus/ImageProcessing2/flip.jpg';

  constructor() {}

  ngOnInit() {}

  loaded(): void {
    console.log(true)
  }
}

Затем у меня есть эта директива, для которой я хотел бы написать несколько тестов:

@Directive({
  selector: '[backgroundLoaded]'
})

export class BackgroundLoadedDirective {
  @Input('backgroundLoaded') set url(value) {
    this.createImage(value);
  };

  get url() {
    return this._url;
  }

  @Output() loaded: EventEmitter<any> = new EventEmitter<any>();

  public img: HTMLImageElement;

  private _url: string;

  @HostBinding('class.background-loaded')
  isLoaded = false;

  createImage(url: string): void {

    // This gets logged as expected
    console.log(url);

    this._url = url;

    this.img = new Image();

    this.img.onload = () => {
      this.isLoaded = true;
      this.load.emit(url);
    };

    this.img.src = url;
  }
}

Тогда у меня есть только этот тест:

describe('BackgroundLoadedDirective', () => {

  let component: UnitTestComponent;
  let fixture: ComponentFixture<UnitTestComponent>;
  let spy: any;

  beforeEach(() => {

    TestBed.configureTestingModule({
      declarations: [
        UnitTestComponent,
        BackgroundLoadedDirective
      ],
      schemas: [NO_ERRORS_SCHEMA],
      providers: [
        {provide: ComponentFixtureAutoDetect, useValue: true}
      ]
    });

    fixture = TestBed.createComponent(UnitTestComponent);
    component = fixture.componentInstance;
  });

  it('should create a fake img tag', () => {

    spy = spyOn(component.backgroundLoaded, 'createImage').and.callThrough();

    expect(component.backgroundLoaded.img).toBeTruthy();
    expect(spy).toHaveBeenCalled();
  });
});

Проблема в том, что тест не говорит:

Expected spy createImage to have been called.

Почему шпион не работает несмотря на вызов функции?

ИЗМЕНИТЬ:

Просто чтобы уточнить, это html тестового компонента, который применяет директиву и дает ему URL-адрес.

<div [urlToBackground]="url" [backgroundLoaded]="url" (loaded)="loaded($event)"></div>

person Chrillewoodz    schedule 31.07.2017    source источник
comment
Может быть, я слепой, но я не вижу, где вы вызываете эту функцию.   -  person lexith    schedule 31.07.2017
comment
Вы ожидали, что .and.callThrough(); вызовет функцию?   -  person Frank Modica    schedule 31.07.2017
comment
я думаю, что он устанавливает URL-адрес в своем шаблоне testComponents, который вызывает его из-за его функции установки. Не могли бы вы это подтвердить?   -  person lexith    schedule 31.07.2017
comment
@lexith Это правильно. Добавлю для уточнения.   -  person Chrillewoodz    schedule 31.07.2017
comment
@FrankModica Функция вызывается всякий раз, когда вызывается установщик. Таким образом, это называется в первый раз, когда URL-адрес изображения оценивается директивой.   -  person Chrillewoodz    schedule 31.07.2017
comment
@FrankModica Я создаю шпиона, сначала обращаясь к экземпляру директивы, используя component.backgroundLoaded, который является @ViewChild тестового компонента. Затем я создаю шпиона для функции createImage. Я не уверен, что слежка за самим установщиком будет иметь какое-либо значение, поскольку это просто вызывает функцию createImage.   -  person Chrillewoodz    schedule 31.07.2017


Ответы (1)


В основном мешают крючки жизненного цикла angulars. Ваш тест просто не заботится о времени.

Чтобы упростить тестирование, инициируйте изменение, а затем проверьте, работает ли ваш сеттер (и вызывает ли функция, за которой вы следите).

Что-то вроде этого:

it('should create a fake img tag', () => {
    let spy: jasmine.Spy = spyOn(component.backgroundLoaded, 'createImage').and.callThrough();

    comp.backgroundLoaded.url = 'foobar';
    fixture.detectChanges(); // wait for the change detection to kick in

    expect(spy).toHaveBeenCalled();
});

Надеюсь, поможет.

(редактировать: удален один detectChanges() для ngOnInit, потому что он здесь не нужен и все равно должен вызываться перед тестом)

person lexith    schedule 31.07.2017
comment
У меня это не сработало, потому что я забыл .and.callThrough();! :D - person Guntram; 12.03.2018