你如何使用Jest来测试img.onerror

mrb*_*000 3 unit-testing reactjs jestjs enzyme

我创建了一个React组件,用于加载图像并确定图像是否成功加载.

import React from 'react';
import PropTypes from 'prop-types';
import { LOADING, SUCCESS, ERROR } from '../helpers';

class Image extends React.Component {
  static propTypes = {
    onError: PropTypes.func,
    onLoad: PropTypes.func,
    src: PropTypes.string.isRequired,
  }

  static defaultProps = {
    onError: null,
    onLoad: null,
  }

  constructor(props) {
    super(props);
    this.state = { imageStatus: LOADING };
    this.initImage();
  }

  componentDidMount() {
    this.image.onload = this.handleImageLoad;
    this.image.onerror = this.handleImageError;
    this.image.src = this.props.src;
  }

  initImage() {
    this.image = document.createElement('img');
    this.handleImageLoad = this.handleImageLoad.bind(this);
    this.handleImageError = this.handleImageError.bind(this);
  }

  handleImageLoad(ev) {
    this.setState({ imageStatus: SUCCESS });
    if (this.props.onLoad) this.props.onLoad(ev);
  }

  handleImageError(ev) {
    this.setState({ imageStatus: ERROR });
    if (this.props.onError) this.props.onError(ev);
  }

  render() {
    switch (this.state.imageStatus) {
      case LOADING:
        return this.renderLoading();
      case SUCCESS:
        return this.renderSuccess();
      case ERROR:
        return this.renderError();
      default:
        throw new Error('unknown value for this.state.imageStatus');
    }
  }
}

export default Image;
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用Jest + Enzyme创建一个测试,以测试图像何时无法加载.

  it('should call any passed in onError after an image load error', () => {
    const onError = jest.fn();
    mount(<Image {...props} src="crap.junk"} onError={onError} />);
    expect(onError).toHaveBeenCalled();
  });
Run Code Online (Sandbox Code Playgroud)

无论我做什么,Jest总能找到一种成功渲染图像的方法.即使将src设置为false仍会以某种方式呈现图像.有谁知道你怎么能强迫玩笑失败图像负载?

jam*_*oks 21

如果您使用@testing-library/reactimg ,您可以在with上触发错误fireEvent.error

const onError = jest.fn();
render(<Image alt="test" src="crap.junk"} onError={onError} />);
fireEvent.error(screen.getByAltText("test"));
expect(onError).toHaveBeenCalled();
Run Code Online (Sandbox Code Playgroud)


Chr*_*ris 8

由于在幕后,document.createElement('img')相当于new Image(),我们可以模拟部分jsdom(由Jest使用)实现,Image以实现你的目标.

(为简单起见,我已将您的组件重命名为ImageComponent)

describe('Callbacks', () => {
  const LOAD_FAILURE_SRC = 'LOAD_FAILURE_SRC';
  const LOAD_SUCCESS_SRC = 'LOAD_SUCCESS_SRC';

  beforeAll(() => {
    // Mocking Image.prototype.src to call the onload or onerror
    // callbacks depending on the src passed to it
    Object.defineProperty(global.Image.prototype, 'src', {
      // Define the property setter
      set(src) {
        if (src === LOAD_FAILURE_SRC) {
          // Call with setTimeout to simulate async loading
          setTimeout(() => this.onerror(new Error('mocked error')));
        } else if (src === LOAD_SUCCESS_SRC) {
          setTimeout(() => this.onload());
        }
      },
    });
  });

  it('Calls onError when passed bad src', done => {
    const onError = ev => {
      expect(ev).toBeInstanceOf(Error);

      // Indicate to Jest that the test has finished
      done();
    };

    mount(<ImageComponent onError={onError} src={LOAD_FAILURE_SRC} />);
  });
});
Run Code Online (Sandbox Code Playgroud)

在这个jsdom问题中解释了原因onerror/ onload从未在浏览器中调用的原因.在同一个问题线程中提供了一个可以影响所有测试的备用修复程序.

  • 这对我有用。但是,看起来好像没有办法在测试套件的末尾取消设置“ Object.defineProperty”,将“ Image.prototype.src”恢复到原来的状态。 (2认同)

And*_*rle 0

你可以嘲笑document.createElement

 it('should call any passed in onError after an image load error', () => {
    const img = {}
    global.document.createElement = (type) => type === 'img' ? img : null
    const onError = jest.fn();
    mount(<Image {...props} src="crap.junk"} onError={onError} />);
    img.onload()
    expect(onError).toHaveBeenCalled();
  });
Run Code Online (Sandbox Code Playgroud)

  • 我放弃。我只是决定使用wrapper.instance(),然后直接调用instance.image.onerror()。我突然意识到我测试过度了。我必须相信 HTMLImageElement 在出现图像错误时会触发 onerror。如果不是,则网络浏览器的源代码存在巨大错误。可能性:零。所以我只是手动触发错误来模拟错误。案件结案。不过还是谢谢。 (2认同)