0

I'm trying to provide a mock request for this class and then expect that history.push is called with some path.

Start.js

import React from 'react'
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { ReactComponent as Arrow } from '../../arrow.svg';


export default function Start() {
    let history = useHistory();
    const doInitializeApp = () => {
        axios.get('http://localhost:8080/api/v1/asap/start')
            .then(res => {
                if (res.data == true) {
                    history.push('/login')
                } else {
                    alert('something went wrong. Could not start the application')
                }
            }).catch(err => {
                alert('something went wrong. Could not contact the server!')
            });
    }
    return (
        <div>
            <div className="container">

                <div className="content">
                    <div id="box">
                        <h1>Welcome</h1>
                        <Arrow id="next" onClick={doInitializeApp} />
                    </div>
                </div>
                
            </div>
        </div>
    );
}
 

And this is my approach for the test

Start.test.js

import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Start from '../components/startscreen/Start';
import { ReactComponent as Arrow } from '../arrow.svg';
import axios from "axios";

Enzyme.configure({ adapter: new Adapter() });
describe('Start', () => {
    it('test axios get reroute the application to path /login', () => {
        const mProps = { history: { push: jest.fn() } };
        const wrapper = shallow(<Start {...mProps} />);
        const arrow = wrapper.find(Arrow);
        const axiosSpy = jest.spyOn(axios, 'get');

        //mock axios
        jest.mock("axios");
        //mock axios response
        axios.get.mockResolvedValue({ data: true });
        //simulate onclick
        arrow.simulate('click');
          
        expect(axiosSpy).toHaveBeenCalled(); --> this pass
        expect(mProps.history.push).toBeCalledWith('/login'); --> this doesn't pass
    })
});

However, the test did not pass because the actual axios.get(url) doesn't take the response which I mocked and it always come to the .catch(err => ... "Could not contact the server!")

What did I do wrong in here ? Because that the code didn't come to the if (res.data===true) so that I also couldn't test whether the history.push is actually called or not.

Betoobb
  • 37
  • 1
  • 8
  • Does [this](https://stackoverflow.com/questions/59553208/enzyme-react-router-how-to-shallow-render-a-component-with-usehistory) answer your question? – Arun Kumar Mohan Feb 25 '21 at 00:15
  • actually not. I did not even pass the code line `if (data ===true)` in my test object, because the mock axios reponse did not work. It always come to the `catch (err =>` code line instead – Betoobb Feb 25 '21 at 09:32
  • Can you update the question with your latest code and share a [CodeSandbox](https://codesandbox.io/) reproducing the issue? – Arun Kumar Mohan Feb 25 '21 at 20:59
  • https://codesandbox.io/s/nice-brown-v26jn this is the code i wanted to test. However in the App.test the test won't work... But the App.js is the class I want to test. – Betoobb Feb 25 '21 at 22:38

1 Answers1

0

Your mocking code is fine. The code in the catch block is getting executed since useHistory() returns undefined (You can confirm this by console.logging the error inside the catch block).

One way to fix it would be to mock useHistory and pass a mock function for history.push. You can then spy on useHistory() to confirm the history.push got called with /login.

import { useHistory } from 'react-router-dom'
// other import statements omitted for brevity

jest.mock('axios')

jest.mock('react-router-dom', () => {
  const fakeHistory = {
    push: jest.fn()
  }

  return {
    ...jest.requireActual('react-router-dom'),
    useHistory: () => fakeHistory
  }
})

const flushPromises = () => new Promise(setImmediate)

describe('Start component', () => {
  test('redirects to /login', async () => {
    const pushSpy = jest.spyOn(useHistory(), 'push')
    axios.get.mockResolvedValue({ data: true })

    const wrapper = shallow(<App />)
    const button = wrapper.find(Arrow)
    button.simulate('click')
    await flushPromises()
    expect(pushSpy).toBeCalledWith('/login')
  })
})

I'm using setImmediate to wait for the async action to complete as suggested here.

Arun Kumar Mohan
  • 11,517
  • 3
  • 23
  • 44
  • Thanks for the answer. I tried your code but I'm getting error at line `const pushSpy = jest.spyOn(useHistory(), 'push')` saying useHistory() is not defined – Betoobb Feb 26 '21 at 10:27
  • @Betoobb Looks like you're not importing `useHistory` in the test. The test should pass when you do. – Arun Kumar Mohan Feb 26 '21 at 21:41
  • I still get this problem. ` TypeError: Cannot read property 'then' of undefined` at the line `const doInitializeApp = () =>{axios.get('http://localhost:8080/api/v1/asap/start')}.then(...)` . It seems like the mocked axios response is not recognized. This problem also happend before. Maybe it cause because of the way i write the function? – Betoobb Feb 27 '21 at 00:03