tylercollier / redux-form-test

Shows how to do unit tests and integration tests with Redux-Form

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to test redux-form

snup77 opened this issue · comments

Hi Tyler,

Thanks for making these tests available for others to use! I'm have difficultly running both unit and integration tests with the code you've provided. For the sake of brevity, I'll only share some of the code for the integration tests I'm trying to run.

confirmation.js

import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import { sendActivationEmail, resetAuthError } from '../../actions';

export const renderField = ({ input, label, type, meta: { touched, error } }) => (
  <fieldset className="form-group">
    <div className={touched && error ? 'has-danger' : ''}>
      <p>Resend Confirmation Instructions</p>
      <input {...input} placeholder={label} type={type} className="form-control"/>
      {touched && error && <span className="error">{error}</span>}
    </div>
  </fieldset>
)

export class Confirmation extends Component {
  componentWillUnmount() {
    this.props.resetAuthError();
  }

  handleFormSubmit({ email }) {
    this.props.sendActivationEmail({ email });
  }

  renderAlert() {
    if (this.props.errorMessage) {
      return (
        <div className="alert alert-danger">
          <strong>Oops!</strong> {this.props.errorMessage}
        </div>
      )
    }
  }

  render() {
    const { handleSubmit } = this.props;
    return (
      <div>
        {this.renderAlert()}
        <form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
          <Field
            label="Email"
            name="email"
            component={renderField}
            type="text"
          />
          <button type="submit" className="btn btn-primary">Resend</button>
        </form>
      </div>
    );
  }
}

function validate(formProps) {
  const errors = {};

  if (!formProps.email) {
    errors.email = 'Please enter an email';
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(formProps.email)) {
    errors.email = 'Please enter a valid email address';
  }

  return errors;
}

function mapStateToProps(state) {
  return { errorMessage: state.auth.error }
}

Confirmation = reduxForm({
  form: 'confirmation',
  validate
})(Confirmation);

Confirmation = connect(mapStateToProps, { sendActivationEmail, resetAuthError })(Confirmation);

export default Confirmation;

confirmation_test.js

import { expect } from 'chai';
import { shallow, mount, unmount } from 'enzyme';
import sinon from 'sinon';

import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';

import reducers from '../../../src/reducers';
import ConfirmationContainer, { ConfirmationComponent, renderField }  from '../../../src/components/auth/confirmation';

const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);

  describe('Container', () => {
    let sendActivationEmail, resetAuthError, props, errorMessage, subject;
    beforeEach(() => {
      sendActivationEmail = sinon.spy();
      resetAuthError = sinon.spy();
      props = {
        sendActivationEmail,
        resetAuthError,
        errorMessage: 'required'
      };

      subject = mount(
        <Provider store={store}>
          <ConfirmationContainer {...props} />
        </Provider>
        )
      });

    it('renders error message', (done) => {
      expect(subject.find('.alert')).to.have.length(1);
      done();
    });

    it('calls sendActivationEmail on submit', (done)=> {
        const form = subject.find('form');
        const input = subject.find('input').first();

        input.simulate('change', { target: { value: 'test@gmail.com' } });
    	form.simulate('submit');
    	expect(sendActivationEmail.callCount).to.equal(1);
        done();
    });

    it('calls resetAuthError on unmount', (done) => {
        subject.unmount();
        expect(resetAuthError.calledOnce).to.equal(true);
        done();
    });
  });

When I run these tests, I get a bunch of error messages. The component works fine, so I'm thinking there is something wrong with how I've structured the code. Any thoughts on what I'm doing wrong?

Thanks!

I'm have difficultly running both unit and integration tests with the code you've provided

First, when you run npm run test in this repo, without adding any of your changes, do all tests pass?

When I run these tests, I get a bunch of error messages.

What are the error messages?

Yes, all tests in the repo pass. Apologies for not initially being clear about that.

I get error messages after I made my changes. Here are the errors I get:

  1) ConfirmationForm Container renders error message:
       AssertionError: expected { Object (component, root, ...) } to have a length of 1 but got 0
        at Context.<anonymous> (test/components/auth/confirmation_test.js:83:46)

  2) ConfirmationForm Container calls sendActivationEmail on submit:

      AssertionError: expected 0 to equal 1
      + expected - actual

      -0
      +1
      
      at Context.<anonymous> (test/components/auth/confirmation_test.js:93:48)

  3) ConfirmationForm Container calls resetAuthError on unmount:

      AssertionError: expected false to equal true
      + expected - actual

      -false
      +true
      
      at Context.<anonymous> (test/components/auth/confirmation_test.js:99:46)

It looks like you are missing mergeProps, as shown in ContactFormContainer.js:

// We'll pass this mergeProps parameter to redux's connect. It's what allows us
// to override as we please during testing.
const mergeProps = (stateProps, dispatchProps, ownProps) =>
	Object.assign({}, stateProps, dispatchProps, ownProps)

ContactFormContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(ContactFormContainer)

That makes the first 2 tests pass. Please see the branch I created to show this off: 6-integration-test. You can pull the branch and examine it's history to see what's changed from what you gave me.

Your 3rd test deals with unmounting, which I'd never tested before. That gave me this error:

Invariant Violation: dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use ReactDOMServer.renderToString() for server rendering.

I was able to resolve this by following the advice from here and here. I extracted out the setup of jsdom into its own file that is run first. You can see this in the npm test script in package.json.

So all 3 of your tests pass for me now.

My tests pass now! Thank for your help, and for your tests.

Glad I could help :-)