12

I'm having a really hard time learning how to use jest. All the tutorials i come across either teach you how to test a script that renders to dom such as <App /> with or without snapshots. Other tutorials goes over how to mock tests with inputs. but I cant seem to find tutorials that explains clearly and give examples that i can use.

For example the script from below i have an idea on how to test the render portion, but i don't know how to test the redux or the rest of the functions.

Could anyone give an example of how to test the below script that i can use as a reference for the rest of the files i need to test in my project?

import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import CustomSearch from '../Components/CustomSearch';
import CustomToolBar from '../Components/CustomToolBar';
import Table from '../Components/Table';
import InsertButton from '../Components/InsertButton';

import UserForm from './UserForm ';

import { fetchUsers, deleteUser } from '../../actions/users';
import setModal from '../../actions/modal';

import TableColumns from '../../constants/data/TableColumns';

class Users extends Component {
  constructor(props) {
    super(props);
    this.onInsert = this.onInsert.bind(this);
    this.onDelete = this.onDelete.bind(this);
    this.onEdit = this.onEdit.bind(this);
    this.props.fetchUsers({ accountId: this.props.userData.account.id, token: props.userData.token });
  }

  onDelete(row) {
    if (confirm(`Are you sure you want to delete user ${row.first} ${row.last}?`)) {
      this.props.deleteUser({
        registered: row.registered,
        id: row.id,
        accountId: this.props.userData.account.id,
        token: this.props.userData.token
      });
    }
  }

  onEdit(row) {
    console.log(row);
    const modal = () => (<UserForm data={row} isEdit />);
    this.props.setCurrentModal(modal, 'Existing User Form');
  }

  onInsert() {
    const modal = () => (<UserForm />);
    this.props.setCurrentModal(modal, 'Add New User');
  }

  render() {
    const options = {
      searchField: (props) => (<CustomSearch {...props} />),
      insertBtn: () => (<InsertButton onClick={this.onInsert} />),
      toolBar: (props) => (<CustomToolBar {...props} />),
      onDelete: this.onDelete,
      onEdit: this.onEdit,
    };
    return (
      <Table data={this.props.users} columns={TableColumns.USERS} options={options} title="Users" />
    );
  }
}

User.propTypes = {
  setCurrentModal: PropTypes.func.isRequired,
  fetchUsers: PropTypes.func.isRequired,
  deleteUser: PropTypes.func.isRequired,
  userData: PropTypes.object.isRequired,
  users: PropTypes.array,
};

const mapStateToProps = (state) => ({
  userData: state.userData.data,
  users: state.tableData.users,
});

const mapDispatchToProps = (dispatch) => ({
  fetchUsers: (data) => dispatch(fetchUsers(data)),
  deleteUser: (data) => dispatch(deleteUser(data)),
  setCurrentModal: (modal, title) => dispatch(setModal(modal, title, null, true)),
});

export default connect(mapStateToProps, mapDispatchToProps)(User);
dwjohnston
  • 11,163
  • 32
  • 99
  • 194
Eric
  • 954
  • 2
  • 14
  • 23
  • Seen as nobody has answers yet I will give you a full explanation when I am free tomorrow on how to do this. Do you know how to do testing in general and it's just the redux part where you connect components you are unsure of? – Martin Dawson Oct 26 '17 at 19:04
  • I have an idea on how to do snapshot testing and the basics thats about it. – Eric Oct 28 '17 at 00:32

1 Answers1

4

You should test raw component because it's clear that redux works so you don't have to test it. If for some reason you want to test mapStateToProps or mapDispatchToProps export them as well and test them separately in isolation.

So if you export your raw component like this:

export { Users }; // here you export raw component without connect(...)
export default connect(mapStateToProps, mapDispatchToProps)(Users);

Then you can test it as a standard react component by importing named export, like

import { Users } from './Users';

describe('Users', () => ....
   it('should render', () => ...

If you would like to test connected component because you don't want shallow rendering and maybe you render a lot of nested connected components, you need to wrap your component with <Provider> and create a store for it.

You can help yourself by using redux-mock-store that will apply middlewares for you.

Everything is very well explained in official redux documentation in Recipes > Writing tests, so my proposal is to read the whole chapter carefully. You can read there also about testing action creators, reducers and even more advanced concepts.

To read more and get better background, I encourage to check these 2 comments below from official redux / react-redux repos.

enter image description here

Direct link to comment: https://github.com/reactjs/react-redux/issues/325#issuecomment-199449298


enter image description here

Direct link to comment: https://github.com/reactjs/redux/issues/1534#issuecomment-280929259


Related question on StackOverflow:

How to Unit Test React-Redux Connected Components?

Dawid Karabin
  • 5,113
  • 1
  • 23
  • 31
  • I've been writing tests for reducers and action creators and simple pure components such as button, container. I thought i had to test these bigger files where we have users and functions. Am i looking at jest wrong? – Eric Oct 25 '17 at 02:53
  • If you want to test methods `onEdit`, `onInsert`, `onDelete`, you can always get the instance of the component and test methods manually, like `const wrapper = mount(; const instance = wrapper.instance(); expect(instance.onDelete()).to...` etc. You can read more about enzyme `instance()` method here: https://github.com/airbnb/enzyme/blob/v2.9.1/docs/api/ReactWrapper/instance.md – Dawid Karabin Oct 25 '17 at 19:55
  • @hinok Don't use mount for unit testing components (which is almost certainly what OP wants to do). Use shallow and dive() instead. – Martin Dawson Oct 26 '17 at 19:01
  • @MartinDawson Agreed, but there are cases when you want mount a component e.g. testing logic in lifecycle hooks. I know that you can fire lifecycle hooks using shallow() but it's not very well documented in docs. PS. Back to my previous comment, why I put mount instead of shallow? The answer is - I just copied code from the enzyme's instance() docs :) https://github.com/airbnb/enzyme/blob/v2.9.1/docs/api/ReactWrapper/instance.md – Dawid Karabin Oct 26 '17 at 20:53
  • @hinok You can use `lifecycleExperimental` to test lifecycle hooks as an option for shallow renderer. You don't need `mount` to test lifecycle anymore. You only should use mount for integration testing now. – Martin Dawson Oct 26 '17 at 21:09