Enzyme: Test click button handler call (with handler declared with row function... or classe property)

Created on 26 Jun 2018  路  4Comments  路  Source: enzymejs/enzyme

Sorry for my english :)

I want to test that if I simulate a click on a button, its handler is called.

I think my problem comes from the fact that I declare my click handler as a property of my component class.

Is there a way to do this unit test without changing my handler declaration ?

My component :

import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchDocuments, getDocuments, getDocumentColumns, cleanFiltersData } from "../../store";

import DocsTab from "../DocsTab";
import { Redirect } from "react-router-dom";

export class DocsList extends Component {
  componentWillMount() {
    const { fetchDocuments } = this.props;
    this.stringQuery = this.getStringQuery();
    if (this.stringQuery) fetchDocuments(this.stringQuery);
  }

  getStringQuery() {
    return this.props.location.search;
  }

  handelNewSearch = e => {
    this.props.cleanFiltersData();
    this.props.history.goBack();
  };

  handelEditSearch = e => {
    this.props.history.goBack();
  };

  render() {
    const { documents, columns } = this.props;

    return this.stringQuery ? (
      <div className="DocsList">
        <button className="edit-search-button" onClick={this.handelEditSearch}>
          Modifier la recherche
        </button>
        <button className="go-back-button" onClick={this.handelNewSearch}>
          Nouvelle recherche
        </button>
        <DocsTab className="aaa" columns={columns} documents={documents} />
      </div>
    ) : (
      <Redirect to="/doc-search" />
    );
  }
}

const mapStateToProps = state => ({
  documents: getDocuments(state),
  columns: getDocumentColumns(state),
});

const mapDispatchToProps = { fetchDocuments, cleanFiltersData };

export default connect(mapStateToProps, mapDispatchToProps)(DocsList);

My Unit test :

import React from "react";
import { shallow } from "enzyme";
import { DocsList } from "./DocsList";
import DocsTab from "../DocsTab";
import { Redirect } from "react-router-dom";

const mockProps = {
  location: { search: "old_DocOBJ=qsdqsd&ecm_RefDoc=sdfsdf&ecm_RefExterne=sdfsdf" },
  history: { push: jest.fn(), goBack: jest.fn() },
  documents: [
    { id: "1", a: "A0", b: "B0", c: "C0", d: "D0" },
    { id: "2", a: "A1", b: "B1", c: "C1", d: "D1" },
    { id: "3", a: "A2", b: "B2", c: "C2", d: "D2" },
    { id: "4", a: "A3", b: "B3", c: "C3", d: "D3" },
  ],
  columns: [
    { id: "a", title: "A" },
    { id: "b", title: "B" },
    { id: "c", title: "C", disabledForList: false },
    { id: "e", title: "E", disabledForList: true },
  ],
  fetchDocuments: jest.fn(),
  cleanFiltersData: jest.fn(),
};

const shallowDocsList = (mockProps, noStringQuery) => {
  return !noStringQuery
    ? shallow(<DocsList {...mockProps} />)
    : shallow(<DocsList {...mockProps} location={{ ...mockProps.location, search: "" }} />);
};

describe("DocsList Component", () => {
  //...
  //...
  it("should render edit search button with correct click handler", () => {
    const docsListWrapper = shallowDocsList(mockProps);
    const handelEditSearchSpy = jest.spyOn(docsListWrapper.instance(), "handelEditSearch");

    const searchButtonWrapper = docsListWrapper.find("button.edit-search-button");
    expect(searchButtonWrapper.length).toBe(1);

    searchButtonWrapper.simulate("click");
    expect(handelEditSearchSpy).toHaveBeenCalled();
  });
});

out put:

expect(jest.fn()).toHaveBeenCalled()

    Expected mock function to have been called.

      at Object.it (src/scripts/components/DocsList/DocsList.test.js:62:33)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
          at <anonymous>

Thank you !

question

All 4 comments

The issue is that you have arrow functions in class fields - which should never happen.

Instead, make normal instance methods, and bind them in the constructor. That way, you can spy on them with spyOn(DocsList.protototype, 'handleEditSearch') etc before shallow-rendering, and everything will work as expected.

Exactly, my question is can I do this test while keeping my handler declared as an arrow functions in class fields.

@ljharb

The issue is that you have arrow functions in class fields - which should never happen.

This is one of the ways to declare a handler in react. it's mentioned in the official documentation, if I'm not mistaken : https://reactjs.org/docs/faq-functions.html

Thanks

I know what's in the docs; but that's not relevant. It's still not correct to put arrow functions in class fields.

The only way you could still test it is to make your wrapper, mock out the class field arrow function, and then forceUpdate the wrapper. It's a hacky and dirty approach to misusing a language feature, and I strongly suggest you instead avoid putting arrow functions in class fields.

Thanks :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aweary picture aweary  路  3Comments

thurt picture thurt  路  3Comments

blainekasten picture blainekasten  路  3Comments

rexonms picture rexonms  路  3Comments

abe903 picture abe903  路  3Comments