【问题标题】:Trying to test a function in Jasmine which returns `expected a spy but got a function`试图在 Jasmine 中测试一个函数,该函数返回“预期是间谍但得到了一个函数”
【发布时间】:2021-05-11 12:31:29
【问题描述】:

该代码基本上适用于仅接受 SVG 的缩略图上传器。问题出在 new FileReader() 和 new Image() 上。执行时无法理解错误 Expected a spy but got a function。我正在尝试测试的功能。该功能用于 NgbModal,显示为上传缩略图的预览目的。上传的缩略图首先加载到模态然后加载到组件

  onFileChanged(file: File): void {
    this.uploadedImageMimeType = file.type;
    this.invalidImageWarningIsShown = false;
    this.invalidTagsAndAttributes = {
      tags: [],
      attrs: []
    };
    if (this.isUploadedImageSvg()) {
      let reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () =>{
        this.imgSrc = reader.result as string;
        this.updateBackgroundColor(this.tempBgColor);
        this.img = new Image();

        this.img.onload = () => {
          //   Setting a default height of 300px and width of
          //   150px since most browsers use these dimensions
          //   for SVG files that do not have an explicit
          //   height and width defined.
          this.setImageDimensions(
            this.img.naturalHeight || 150,
            this.img.naturalWidth || 300);
        };
        this.img.src = this.imgSrc;
        this.uploadedImage = this.imgSrc;
        this.invalidTagsAndAttributes = (
          this.svgSanitizerService.getInvalidSvgTagsAndAttrsFromDataUri(
            this.imgSrc));
        this.tags = this.invalidTagsAndAttributes.tags;
        this.attrs = this.invalidTagsAndAttributes.attrs;
        if (this.tags.length > 0 || this.attrs.length > 0) {
          this.reset();
        }
      };
    } else {
      this.reset();
      this.invalidImageWarningIsShown = true;
    }
  }

我为它写的测试

class MockImageObject {
  source = null;
  onload = null;
  constructor() {
    this.onload = () => {
      return 'Fake onload executed';
    };
  }
  set src(url) {
    this.onload();
  }
}

class MockReaderObject {
  result = null;
  onload = null;
  constructor() {
    this.onload = () => {
      return 'Fake onload executed';
    };
  }
  readAsDataURL(file) {
    this.onload();
    return 'The file is loaded';
  }
}

it('should load a image file in onchange event and save it if it\'s a' +
    ' svg file', fakeAsync(() => {
    // This is just a mocked base 64 in order to test the FileReader event
    // and its result property.
    const dataBase64Mock = 'PHN2ZyB4bWxucz0iaHR0cDo';
    const arrayBuffer = Uint8Array.from(
      window.atob(dataBase64Mock), c => c.charCodeAt(0));
    const file = new File([arrayBuffer], 'thumbnail.png', {
      type: 'image/svg+xml'
    });
    component.uploadedImageMimeType = file.type;
    component.invalidImageWarningIsShown = false;
    component.invalidTagsAndAttributes = {
      tags: [],
      attrs: []
    };
    // This throws "Argument of type 'mockReaderObject' is not assignable to
    // parameter of type 'HTMLImageElement'.". This is because
    // 'HTMLImageElement' has around 250 more properties. We have only defined
    // the properties we need in 'mockReaderObject'.
    // @ts-expect-error
    spyOn(window, 'FileReader').and.returnValue(new MockReaderObject());
    const image = document.createElement('img');
    spyOn(window, 'Image').and.returnValue(image);

    // This throws "Argument of type 'mockImageObject' is not assignable to
    // parameter of type 'HTMLImageElement'.". This is because
    // 'HTMLImageElement' has around 250 more properties. We have only defined
    // the properties we need in 'mockImageObject'.
    // @ts-expect-error
    spyOn(window, 'Image').and.returnValue(new mockImageObject());
    // ---- Dispatch on load event ----
    image.dispatchEvent(new Event('load'));

    expect(component.invalidImageWarningIsShown).toBe(false);
    component.onInvalidImageLoaded();

    expect(component.invalidImageWarningIsShown).toBe(true);
    component.onFileChanged(file);

    // ---- Dispatch on load event ----
    expect(component.invalidTagsAndAttributes).toEqual({
      tags: [],
      attrs: []
    });
    expect(component.uploadedImage).toBe(null);
    expect(component.invalidImageWarningIsShown).toBe(false);

    // ---- Save information ----
    component.confirm();
    expect(component.confirm).toHaveBeenCalled();
  }));

非常感谢任何帮助!

【问题讨论】:

    标签: angular typescript unit-testing karma-jasmine


    【解决方案1】:

    问题是下面语句中的component.confirm不是Spy而是一个函数。

    expect(component.confirm).toHaveBeenCalled();
    

    请参阅 Jasmine 文档中的 toHaveBeenCalled(expected)

    解决方案

    在这一行前面的某处,您需要创建一个Spy,如下所示:

    spyOn(component, 'confirm').and.callThrough();
    

    但是请注意,这个expect 永远不会失败,因为您在之前的行中的测试中明确调用了component.confirm()

    【讨论】:

    • 感谢您的解决方案的帮助!
    • ,如果可能的话,您能否进一步看一下以帮助我完全覆盖此功能?好像我无法在测试中点击 img.onload?
    • 由于您的测试在fakeAsync 区域中运行,您可以尝试在component.onFileChanged(file); 之后从@angular/core/testing 调用flush()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 2016-05-27
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 2015-06-22
    相关资源
    最近更新 更多