Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

unit testing - How do you use Jest to test img.onerror

I've created a React component that loads an image and determines if the image loaded successfully or not.

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;

I'm trying to create a test using Jest + Enzyme to test when an image fails to load.

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

No matter what I do, Jest always finds a way to successfully render the image. Even setting src to false still renders an image somehow. Does anyone know how in the heck you can force jest to fail an image load?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Since behind the scenes, document.createElement('img') is equivalent to new Image(), we can mock part of the jsdom (used by Jest) implementation of Image to achieve what you're going for.

(For simplicity's sake, I've renamed your component to 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} />);
  });
});

The reason onerror/onload is never called as it would be in a browser is explained in this jsdom issue. An alternate fix that would affect all your tests is offered up in the same issue thread.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...