Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing React component with dynamic import (Enzyme/Mocha)

I'm trying to test a React component using Mocha and Enzyme that uses a dynamic import to load a module.

When I try to test the logic that relies on the dynamic import I get incorrect results. The problem is that the async functions don't finish before the test finishes so I can never get accurate results.

How can I handle this scenario?

Component

import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';

// styles

import styles from './PasswordStrengthIndicator.scss';

class PasswordStrengthIndicator extends React.Component {
  static defaultProps = {
    password: undefined,
    onPasswordChange: undefined,
  }

  static propTypes = {
    password: PropTypes.string,
    onPasswordChange: PropTypes.func,
  }

  constructor() {
    super();

    this.state = {};
  }

  componentWillMount() {
    this.handlePasswordChange();
  }

  componentWillReceiveProps(nextProps) {
    const password     = this.props.password;
    const nextPassword = nextProps.password;

    if (password !== nextPassword) {
      this.handlePasswordChange();
    }
  }

  render() {
    const strength = this.state.strength || {};
    const score    = strength.score;

    return (
      <div className={ styles.passwordStrength }>
        <div className={ classNames(styles.score, styles[`score-${score}`]) } />
        <div className={ styles.separator25 } />
        <div className={ styles.separator50 } />
        <div className={ styles.separator75 } />
      </div>
    );
  }

  // private

  async determineStrength() {
    const { password } = this.props;
    const zxcvbn = await import('zxcvbn');

    let strength = {};

    if (password) strength = zxcvbn(password);

    return strength;
  }

  async handlePasswordChange() {
    const { onPasswordChange } = this.props;
    const strength = await this.determineStrength();

    this.setState({ strength });

    if (onPasswordChange) onPasswordChange(strength);
  }
}

export default PasswordStrengthIndicator;

Test

describe('when `password` is bad', () => {
  beforeEach(() => {
    props.password = 'badpassword';
  });

  it.only('should display a score of 1', () => {
    const score = indicator().find(`.${styles.score}`);

    expect(score.props().className).to.include(styles.score1); // should pass
  });
});
like image 975
anthonator Avatar asked Sep 12 '26 21:09

anthonator


1 Answers

I was able to accomplish this with a -- something.

I switched the test that relies on the dynamic import to be asynchronous. I then created a function that renders the component and returns a promise that dynamically imports the module I'm trying to import in the component.

const render = () => {
  indicator = shallow(
    <PasswordStrengthIndicator { ...props } />,
  );

  return (
    Promise.resolve()
      .then(() => import('zxcvbn'))
  );
};

I believe this is relying on the same concept as just waiting since import('zxcvbn') will take a similar enough amount of time to import in both places.

Here's my test code:

describe('when `password` is defined', () => {
  describe('and password is bad', () => {
    beforeEach(() => {
      props.password = 'badpassword';
    });

    it('should display a score of 1', (done) => {
      render()
        .then(() => {
          const score = indicator.find(`.${styles.score}`);

          expect(score.props().className).to.include(styles.score1);

          done();
        });
    });
  });
});

This ended up working out because it prevented me from having to stub and I didn't have to change my component's implementation to better support stubbing. It is also less arbitrary than waiting x ms.

I'm going to leave this question open for now as there are probably better solutions that the community can provide.

like image 169
anthonator Avatar answered Sep 16 '26 18:09

anthonator



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!