Angular - 单元测试间谍无法识别该函数已被调用

Posted

技术标签:

【中文标题】Angular - 单元测试间谍无法识别该函数已被调用【英文标题】:Angular - unit test spy are not recognising that the function has been called 【发布时间】:2018-01-07 03:06:48 【问题描述】:

我有一个用于测试指令的测试组件:

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>

【问题讨论】:

也许我是盲人,但我看不出你在哪里调用这个函数。 您是否期望.and.callThrough(); 会调用该函数? 我认为他在他的 testComponents 模板中设置了 url,由于他的 setter 函数,该模板会调用它。你能确认一下吗? @lexith 没错。我会添加它以进行澄清。 @FrankModica 每当调用 setter 时都会调用该函数。所以第一次调用图片url被指令求值。 【参考方案1】:

基本上干扰的是 angulars 生命周期钩子。您的测试在时间方面不够重视。

为了更容易测试,触发更改,然后测试您的 setter 是否有效(并调用您正在监视的函数)。

类似这样的:

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();
);

希望对你有帮助。

(编辑:为ngOnInit 删除了一个detectChanges(),因为这里不需要它,无论如何都应该在测试之前调用它)

【讨论】:

它对我不起作用,因为我忘记了.and.callThrough();! :D

以上是关于Angular - 单元测试间谍无法识别该函数已被调用的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jasmine 间谍进行 Angular 2 组件测试“没有 Http 提供者!”错误

Angular中的单元测试点击事件

来自 node_modules 的 Angular 单元测试 spyOn 类

角色与间谍的间谍

Angular 单元测试:使用泛型创建组件

如何封装AngularJS函数但仍然可以测试它