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

时间:2017-06-09 16:28:05

标签: 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;

我尝试使用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();
  });

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

4 个答案:

答案 0 :(得分:6)

由于幕后,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} />);
  });
});

jsdom issue中解释了onerror / onload永远不会被调用的原因。可以影响所有测试的备用修补程序在同一issue thread中提供。

答案 1 :(得分:1)

如果您使用 @testing-library/react,您可以使用 img 触发 fireEvent.error 上的错误:

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

答案 2 :(得分: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();
  });

答案 3 :(得分:0)

我使用笑话 + 酶。我只是手动调用 onError 方法,如下所示:

ToolbarOnDestinationChangedListener