How to mock useLocation correctly? - javascript

I have a component that uses useLocation hook to get the path from the URL.
const { pathname } = useLocation();
useEffect(() => { }, [pathname]);
While I am trying to mock the location using ,
import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '#testing-library/react';
import { shallow } from 'enzyme';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: 'https://URL/'
})
}));
describe('<ExampleComponent />', () => {
it('should render correctly', () => {
shallow(<ExampleComponent />);
});
});
I am getting this error while I run the test,
TypeError: Cannot read property 'location' of undefined

Try mocking the useLocation as jest.fn().mockImplementation
jest.mock('react-router', () => ({
...jest.requireActual("react-router") as {},
useLocation: jest.fn().mockImplementation(() => {
return { pathname: "/testroute" };
})
}));

Below is how I have done this in my tests. * Note I am using typescript
import routeData from 'react-router';
describe('Login Page UnitTests', () => {
const useLocation = jest.spyOn(routeData, 'useLocation');
beforeEach(() => {
useLocation.mockReturnValue({ search: 'testQueryParameters'} as any);
});
// Add unit tests
}
Ensure that you clear the mock to avoid issue with data in subsequent tests

The correct way to mock useLocation is below:
import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '#testing-library/react';
import { MemoryRouter} from 'react-router-dom';
import { shallow } from 'enzyme';
const renderComponent = () => {
return (
<MemoryRouter
initialEntries={["/one", "/two", { pathname: 'https://URL/' }]}
initialIndex={1}>
<ExampleComponent />
</MemoryRouter>
);
}
describe('<ExampleComponent />', () => {
it('should render correctly', () => {
shallow(renderComponent());
});
});

Related

How to test that a function prop was called in react-testing-library

There are 3 files:
File 1: helpers.js
export const helpers = () => ({
bar: () => 'Bar was called',
});
File 2: TestComponent.js
import React from 'react';
import { helpers } from './helpers';
const TestComponent = () => {
const { bar } = helpers();
return (
<><button onClick={bar}/></>
);
};
export default TestComponent;
File 3: TestComponent.test.js
import React from 'react';
import userEvent from '#testing-library/user-event';
import { screen, render } from '#testing-library/react';
import TestComponent from './TestComponent';
import { helpers } from './helpers';
jest.mock('./helpers', () => ({
helpers: jest.fn(),
}));
test('bar is called', () => {
helpers.mockImplementation(() => ({
bar: jest.fn(),
}));
render(
<TestComponent />,
);
userEvent.click(screen.getByRole('button'));
expect(???????).toHaveBeenCalled();
});
This line is the key:
expect(???????).toHaveBeenCalled();
The question: How can I test if bar function was called? I was expecting that something similar to expect(helpers().bar) would work. But it doesn't.
save the function in a variable and use it in expect
test('bar is called', () => {
const bar = jest.fn()
helpers.mockImplementation(() => ({bar}));
render(
<TestComponent />,
);
userEvent.click(screen.getByRole('button'));
expect(bar).toHaveBeenCalled();
});

React + Redux : Test failing with jest

I wonder why the test is failing when I use it with redux hooks:
The code is working finem but the tests are failing for some reason. I am unable to test if the component is being rendered or not.
Component:
import React, { useEffect, useState } from 'react';
import { fetchAllApis } from '../../../redux/actions/marketplace/marketplaceActions';
import { useDispatch, useSelector, connect } from 'react-redux';
import ApiCard from '../ApiCard/ApiCard';
import Spinner from '../../../components/Extras/Spinner/Spinner';
const ApiSection = ({ apiList, error, loading, categories }) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchAllApis({ page, category: categories }));
}, [dispatch, categories]);
const renderApiCards = () => {
return apiList.map((each) => (
<ApiCard key={each.apiId} info={each} data-test="ApiCard" />
));
};
if (loading) {
return <Spinner data-test="Spinner" />;
}
if (error) {
return <h1 data-test="Error">Error while fetching</h1>;
}
return (
<div className="ApiSection" data-test="ApiSection">
<div className="ApiSection__cards">{renderApiCards()}</div>
</div>
);
};
const mapStateToProps = ({ marketplaceApiState }) => {
const { apiList, error, loading } = marketplaceApiState;
return {
error,
loading,
apiList: Object.values(apiList),
};
};
export default connect(mapStateToProps)(ApiSection);
Here is the test for the above component:
Test:
import React from 'react';
import { mount } from 'enzyme';
import ApiListSection from './ApiListSection';
import { findByTestAttr, createTestStore } from '../../../../testUtils';
import { Provider } from 'react-redux';
const setup = (props = {}) => {
let initialState = {
marketPlaceState: {
apiList: {
a: { apiId: 'a', name: 'name', description: 'desc', categories: 'cat'}
},
},
};
const store = createTestStore(initialState);
const wrapper = mount(
<Provider store={store}>
<ApiListSection {...props} />
</Provider>
);
return wrapper;
};
describe('ApiListSection Component', () => {
let component;
beforeEach(() => {
component = setup();
});
// assertions
it('Should render without failing', () => {
const apiSection = findByTestAttr(component, 'ApiSection');
expect(apiSection.length).toBe(1); // <===== FAILING HERE !!!!!
});
});
I would really appreciate the help, thanks in advance

Jest + react-navigation not locating route / params

I am trying to write a test for an app using react-navigation and I am running into issues of the route and params being read correctly.
I am getting an error of
TypeError: Cannot read property 'params' of undefined
on const [leadId] = useState(route.params.leadId);
my components looks like
export default function AComponent() {
const route = useRoute();
const navigation = useNavigation();
const dispatch = useDispatch();
const [leadId] = useState(route.params.leadId);
}
I have tried following https://callstack.github.io/react-native-testing-library/docs/react-navigation/ but I received Warning: React.createElement: type is invalid when wrapping the component.
My test looks like
import React from 'react';
import { Provider } from 'react-redux';
import { NavigationContainer } from '#react-navigation/native';
import { render, fireEvent, cleanup } from 'react-native-testing-library';
import configureMockStore from 'redux-mock-store';
import AComponent from 'components/contact/AComponent';
const mockStore = configureMockStore([]);
describe('<AComponent />', () => {
let getByTestId, store;
beforeEach(() => {
store = mockStore({});
({ getByTestId } = render(
<Provider store={store}>
<AComponent />
</Provider>
));
});
});
my mock is
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: jest.fn(),
};
});
I am not sure if I am wrapping the components incorrectly, or if I am missing something else.
Any ideas or help would be greatly appreciated.
Thanks.
Hey I just solved this myself and here is my solution
Change
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: jest.fn(),
};
});
To
jest.mock('#react-navigation/native', () => ({
...jest.requireActual('#react-navigation/native'),
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: () => ({
params: {
<yourParamName>: '<paramValue>',
<yourParamName2>: '<paramValue2>',
etc...
}
}),
}));
In my case I put this code block into my setup.ts file and then in my jest config inside of package.json I pointed to it.
Example
"setupFiles": [
"./node_modules/react-native-gesture-handler/jestSetup.js",
"./jest/setup.ts"
]
Then in the test itself
const navigation = { navigate: jest.fn() };
const { getByTestId, getByText, queryByTestId } = render(<App navigation={navigation}/>);

How to compare (anonymous) function in jest tests?

I am having the following React component connected to a redux store.
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
import { connect } from 'react-redux'
import { getWeather } from './actions/WeatherActions'
import WeatherComponent from './components/weatherComponent/WeatherComponent'
import { get } from 'lodash'
export class App extends Component {
componentDidMount () {
this.props.dispatch(getWeather())
}
render () {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<WeatherComponent
weather={{
location: get(this.props.weatherReducer.weather, 'name'),
temp: get(this.props.weatherReducer.weather, 'main.temp')
}}
/>
</div>
)
}
}
export default connect((store) => {
return {
weatherReducer: store.weatherReducer,
}
})(App)
This component is dispatching the getWeather action using the componentDidMount callback.
The getWeather action is returning an anonymous method upon resolving the axios promise.
import { GET_WEATHER_DONE, GET_WEATHER_ERROR } from './ActionTypes'
import axios from 'axios'
export function getWeather () {
let endpoint = 'http://api.openweathermap.org/data/2.5/weather?q=London&appid=2a345681ddcde393253af927097f5747'
return function (dispatch) {
return axios.get(endpoint)
.then((response) => {
return dispatch({
type: GET_WEATHER_DONE,
payload: response.data
})
})
.catch((error) => {
return dispatch({
type: GET_WEATHER_ERROR,
payload: error.response.data,
statuscode: error.response.status
})
})
}
}
No I am trying to write a unit test verifying the getWeather action is being dispatched upon mounting. This tests looks as follows and passes.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as actions from './actions/WeatherActions'
describe('app container', () => {
const store = configureMockStore([thunk])({
weatherReducer: {
weather: {}
}
})
const dispatchSpy = jest.fn()
store.dispatch = dispatchSpy
it('dispatches getWeather() action upon rendering', () => {
ReactDOM.render(<App store={store} />, document.createElement('div'))
expect(dispatchSpy.mock.calls[0][0].toString()).toEqual(actions.getWeather().toString())
})
})
Because of the action returning an anonymous method, I need to call the toString method upon my mock to compare the actions.
I recreated this test using snapshot testing.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
describe('app container', () => {
const store = configureMockStore([thunk])({
weatherReducer: {
weather: {}
}
})
const dispatchSpy = jest.fn()
store.dispatch = dispatchSpy
it('dispatches correct actions upon rendering', () => {
ReactDOM.render(<App store={store} />, document.createElement('div'))
let tree = dispatchSpy.mock.calls.toString()
expect(tree).toMatchSnapshot();
})
})
Again I need to call the toString method, resulting in the following snapshot.
// Jest Snapshot v1,
exports[`app container dispatches correct actions upon rendering 1`] = `
"function (dispatch) {
return _axios2.default.get(endpoint).
then(response => {
return dispatch({
type: _ActionTypes.GET_WEATHER_DONE,
payload: response.data });
}).
catch(error => {
return dispatch({
type: _ActionTypes.GET_WEATHER_ERROR,
payload: error.response.data,
statuscode: error.response.status });
});
}"
`;
Now when running coverage, using the yarn test -- --coverage, my test is failing because of istanbul adding text to my action. The output looks as follows:
FAIL src/App.snapshot.test.js
● app container › dispatches correct actions upon rendering
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- Snapshot
+ Received
-"function (dispatch) {
- return _axios2.default.get(endpoint).
- then(response => {
- return dispatch({
- type: _ActionTypes.GET_WEATHER_DONE,
- payload: response.data });
+"function (dispatch) {/* istanbul ignore next */cov_2rypo7bhf.f[1]++;cov_2rypo7bhf.s[2]++;
+ return (/* istanbul ignore next */_axios2.default.get(endpoint).
+ then(response => {/* istanbul ignore next */cov_2rypo7bhf.f[2]++;cov_2rypo7bhf.s[3]++;
+ return dispatch({
+ type: /* istanbul ignore next */_ActionTypes.GET_WEATHER_DONE,
+ payload: response.data });
- }).
- catch(error => {
- return dispatch({
- type: _ActionTypes.GET_WEATHER_ERROR,
- payload: error.response.data,
- statuscode: error.response.status });
+ }).
+ catch(error => {/* istanbul ignore next */cov_2rypo7bhf.f[3]++;cov_2rypo7bhf.s[4]++;
+ return dispatch({
+ type: /* istanbul ignore next */_ActionTypes.GET_WEATHER_ERROR,
+ payload: error.response.data,
+ statuscode: error.response.status });
- });
+ }));
}"
at Object.it (src/App.snapshot.test.js:21:18)
at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
The main problem I am facing is the fact that I need to call the toString method for comparison. What is the correct method for comparing (anonymous) functions in jest testing?
Full source can be found at https://github.com/wvanvlaenderen/react-redux-weathercomponent
So I was able test calls on dispatch by mocking the getWeather action in my test, and verifying the type of the return value on individual calls.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as actions from './actions/WeatherActions'
import { spy } from 'sinon'
describe('app container', () => {
const store = configureMockStore([thunk])({
weatherReducer: {
weather: {}
}
})
const dispatchSpy = spy(store, 'dispatch')
actions.getWeather = jest.fn().mockImplementation(() => {
return {type: 'fetching weather'}
})
it('dispatches getWeather() action upon rendering', () => {
ReactDOM.render(<App store={store} />, document.createElement('div'))
expect(dispatchSpy.firstCall.returnValue.type).toEqual('fetching weather')
})
})
Snapshot testing was achieved by rendering the call tree on the dispatch spy.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { spy } from 'sinon'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as actions from './actions/WeatherActions'
describe('app container', () => {
const store = configureMockStore([thunk])({
weatherReducer: {
weather: {}
}
})
const dispatchSpy = spy(store, 'dispatch')
actions.getWeather = jest.fn().mockImplementation(() => {
return {type: 'fetching weather'}
})
it('dispatches correct actions upon rendering', () => {
ReactDOM.render(<App store={store} />, document.createElement('div'))
expect(dispatchSpy.getCalls()).toMatchSnapshot();
})
})
When testing a redux thunk using Jest, i've used expect.any(Function). All assertion libs have something like this.
ex)
action
const toggleFilter = (toggle, isShown) => {
return dispatch => {
toggle === 'TOGGLE ONE'
? dispatch(toggleSwitchOne(isShown))
: dispatch(toggleSwitchTwo(isShown));
};
};
Test file:
beforeEach(() => {
store = mockStore({ showFruit: false, showVeg: false });
dispatch = jest.fn();
getState = () => store;
})
it('will dispatch action to toggle switch', () => {
let res = toggleFilter(type, isShown)(dispatch, getState);
expect(dispatch).toHaveBeenCalledTimes(1);
expect(dispatch).toHaveBeenCalledWith(expect.any(Function));
});

assert true if method has been called using avajs

I am trying to create a unit test using react,ava, etc..I am having issue creating a simple unit test to check if a method was called. My test should pass if the method has been called. However when i check the code coverage I get a message saying "function not covered". Below is the code I am using to test it.
import test from 'ava';
import React from 'react';
import { Cart } from 'components/Cart/CartDisplay';
import { shallow } from 'enzyme';
let props;
test.beforeEach(() => {
props = {
popError: () => {},
message: '',
count: 2,
displayCart:() => {},
onClose:() => {}
};
});
test('renders okay?', (t) => {
shallow(
<Cart {...props} />
);
t.pass('yes');
});
test('Cart Displayed okay?', (t) => {
props.displayCart();
t.pass('yes');
});
What am I doing wrong?
After a couple of tries, I was able to figure it out:
import test from 'ava';
import React from 'react';
import { Cart } from 'components/Cart/CartDisplay';
import { shallow,mount } from 'enzyme';
import sinon from 'sinon';
import {expect} from 'chai';
let props;
test.beforeEach(() => {
props = {
popError: () => {},
message: '',
count: 2,
displayCart:() => {},
onClose:() => {}
};
});
test('Cart Display called?', t => {
sinon.spy(Cart.prototype, 'cartDisplay');
const wrapper = mount(<BannerMessage />);
expect(Cart.prototype.componentDidMount.calledOnce).to.equal(true);
})

Categories