2

I wrote a simple unit test for the following. I am new to React JS testing - Trying to run a test using jest and enzyme.

     render() {
  return (
    <div>
      <div className="not-found">
      <div className='_2'>WAS NOT FOUND</div>
            <div onClick={() => {window.history.back()}} className='not-found- 
 btn' href='/'>GO BACK</div>

) } }

The file looks simple, there are no props and the only thing not being covered when the test is running is onClick . How could I test onClick and make sure the test is 100 % covered. Thanks

 <div onClick={() => {window.history.back()}} className='not-found- 
 btn' href='/'>GO BACK</div>

file.test.js

  // jest mock functions (mocks this.props.func)
   const onClick =  jest.fn();
 // defining this.props
 const baseProps = {
  onClick,
}

describe(' Test', () => {
let wrapper;
let tree;

 beforeEach(() => wrapper = shallow(<Component{...baseProps } />));
// before each test, shallow mount the Component

it('should render correctly', () => {  
tree = renderer.create(<NotFound {...baseProps} />)
let treeJson = tree.toJSON()
expect(treeJson).toMatchSnapshot();
tree.unmount()
});

       it('calls onClick event ', () => {
  const mockOnClick = jest.fn();
 const wrapper = shallow(
    <NotFound onClick={mockOnClick} className='not-found-btn' />
  );
  const component = wrapper.shallow();
  component.find('GO BACK').simulate('click');
  expect(mockOnClick.mock.calls.length).toEqual(1);
skyboyer
  • 22,209
  • 7
  • 57
  • 64
userr2591
  • 81
  • 1
  • 3
  • 12
  • onClick={() => {window.history.back()}} You are creating an anonymous function here which you will not be able to test. You will not be able to reach 100% coverage. You can test that window.history.back() was called, but not the event function itself. – Chase Feb 04 '19 at 19:36
  • Thanks. i am new to unit testing. How could i test that ? – userr2591 Feb 04 '19 at 20:11

2 Answers2

4

I'd avoid using window history and instead use react-router-dom for MPAs. In addition, instead of using an anonymous function, you can use a PureComponent class (it's similar to a Component class, but it doesn't update state) with a method class function.

Working example: https://codesandbox.io/s/j3qo6ppxqy (this example uses react-router-dom and has a mix of integration and unit testing -- see the tests tab at the bottom of the page to run the tests and look for __test__ folders to see the code)

components/NotFound/notfound.js

import React, { PureComponent } from "react";
import { Button } from "antd";

export default class NotFound extends PureComponent {
  handlePageBack = () => this.props.history.push("/");

  render = () => (
    <div className="notfound">
      <h1>404 - Not Found!</h1>
      <Button type="default" onClick={this.handlePageBack}>
        Go Back
      </Button>
    </div>
  );
}

components/NotFound/__tests__/notfound.test.js (as mentioned here, you can also test the class method, if desired)

import React from "react";
import { shallowComponent } from "../../../tests/utils";
import NotFound from "../notfound";

const mockGoBack = jest.fn();

const initialProps = {
  history: {
    goBack: mockGoBack
  }
};

/* 
 the shallowComponent function below is a custom function in "tests/utils/index.js" that 
 simplifies shallow mounting a component with props and state
 */
const wrapper = shallowComponent(<NotFound {...initialProps} />);

describe("Not Found", () => {
  it("renders without errors", () => {
    const notfoundComponent = wrapper.find("div.notfound");
    expect(notfoundComponent).toHaveLength(1);
  });

  it("pushes back a page when Go Back button is clicked", () => {
    wrapper.find("Button").simulate("click");
    expect(mockGoBack).toHaveBeenCalled();
  });
});
Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51
  • Matt thanks again for your help. You answered a previous question already last week and was really helpful. I will look into it later. Also I posted this one as well which i don't know why im getting an error – userr2591 Feb 05 '19 at 00:23
  • https://stackoverflow.com/questions/54518329/expectreceived-toequalexpected-error – userr2591 Feb 05 '19 at 00:23
  • @userr2591 Please provide a codesandbox example with the code not working. You can fork my example above and use that as a template. – Matt Carlotta Feb 05 '19 at 00:28
  • The codesandbox you've linked isn't set up for testing nor is it implemented. Please fork my sandbox above (it's already set up for testing). And make sure your code is fully implemented (even if it throws errors/warnings). The more complete your example, the quicker I can help you. – Matt Carlotta Feb 05 '19 at 00:51
  • https://codesandbox.io/embed/k2qypovm95 all set. i used your files.. new to codesandbox as well – userr2591 Feb 05 '19 at 00:57
  • I tried to run your answer but it will affect a lot of files and make them fail. – userr2591 Feb 05 '19 at 12:37
1

window.history.back is being called, but it has a delay time. I can make it work using a Promise:

const Component = ()=> (<div>
  <button onClick={()=> window.history.back()} className="btn btn-back">
    Back
  </button>
</div>)

Component.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
     
const delayAction = (fn, time = 1000) =>
      new Promise((resolve) => {
        fn();
        setTimeout(() => {
          resolve();
        }, time);
      });

let container = null;

describe("App tests", () => {
  afterEach(() => {
    //unmount Component...
  });

  beforeEach(() => {
    //mount Component
  });

  it("should call history.back()", async (done) => {
    const btnBack = container.querySelector(".btn-back");
    await act(() =>
      delayAction(() => btnBack.dispatchEvent(new MouseEvent("click", { bubbles: true })))
    );
    // asserts..
    
    done();
  });

});
David Buck
  • 3,752
  • 35
  • 31
  • 35
lissettdm
  • 12,267
  • 1
  • 18
  • 39