I am trying to start writing tests using jest and react-testing-library but when I try to import a react component which should be wrapped in QueryClientProvider, It throws the error
TypeError: _reactQuery.QueryClient is not a constructor
18 | ]
19 |
> 20 | const createTestQueryClient = () => new QueryClient({
| ^
21 | defaultOptions: {
22 | queries: {
23 | retry: false,
//testUtils.tsx
import { render } from '#testing-library/react'
import { rest } from 'msw'
import * as React from 'react'
import {QueryClient, QueryClientProvider } from 'react-query'
export const handlers = [
rest.get(
'*/react-query',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
name: 'mocked-react-query'
})
)
}
)
]
const createTestQueryClient = () => new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
export function renderWithClient(ui: React.ReactElement) {
const testQueryClient = createTestQueryClient()
// const testQueryClient = new QueryClient();
const { rerender, ...result } = render(
<QueryClientProvider client={testQueryClient}>{ui}</QueryClientProvider>
)
return {
...result,
rerender: (rerenderUi: React.ReactElement) =>
rerender(
<QueryClientProvider client={testQueryClient}>{rerenderUi}</QueryClientProvider>
),
}
}
export function createWrapper() {
const testQueryClient = createTestQueryClient()
// const testQueryClient = new QueryClient();
return ({ children }: {children: React.ReactNode}) => (
<QueryClientProvider client={testQueryClient}>{children}</QueryClientProvider>
)
}
//OrgDetails.test.tsx
import { render, screen } from "#testing-library/react";
import {QueryClient, QueryClientProvider} from "react-query";
import OrgDetails from "./OrgDetails";
import { renderWithClient } from './testUtils'
var MockData = {
organization_id: "test",
name: "test",
installations: [
{
ID: 24148217,
CreatedAt: "2022-08-26T09:34:40.220008Z",
UpdatedAt: "2022-08-26T09:34:40.220008Z",
DeletedAt: null,
organizationID: "test",
accountID: 35756816,
targetType: "User",
},
],
environments: null,
kube_integrations: null,
basic_integrations: null,
oidc_integrations: null,
};
jest.mock("react-query", () => ({
useQuery: jest
.fn()
.mockReturnValue({ data: { ...MockData }, isLoading: false, error: {} }),
}));
test("show correct org name", () => {
const result = renderWithClient(<OrgDetails />)
});
Related
Im facing this issue in Sonarqube after pushing my code
How to resolve this issue?
** App.test.js**
import { fireEvent, render, screen } from "#testing-library/react";
import { Provider } from "react-redux";
import App from "./App";
import { Router } from "react-router-dom"
import configureStore from 'redux-mock-store';
import { createHashHistory as createHistory } from 'history';
const mockStore = configureStore([]);
const initialState = {
login: {
access: "access123",
refresh: "refresh123"
},
menu: {
data: [
{
id: 'home',
title: 'Home',
isTvFocus: true,
isActive: true
},
{
id: 'mylist',
title: 'My list',
isTvFocus: false,
isActive: false
},
{
id: 'search',
title: 'Search',
isTvFocus: false,
isActive: false
}
]
},
}
const store = mockStore(initialState);
export const renderWithState = (
ui,
{ initialState, ...renderOptions } = {}
'initialState' is already declared in the upper scope.Why is this an issue?
) => {
const history = createHistory();
const Wrapper = ({ children }) => (
<Provider store={store}><Router history={history}>{children}</Router></Provider>
);
return render(ui, { wrapper: Wrapper, ...renderOptions });
};
jest.mock('./components/Menu',() => () => <div data-testid="menu-id">Home</div> )
test("renders app component with state", () => {
const { getByText, getByTestId } = renderWithState(<App />, { initialState });
screen.debug();
expect(getByText(/Home/i).textContent).toBe("Home");
expect(getByTestId(/menu-id/i)).toBeInTheDocument();
fireEvent.keyDown(getByText(/Home/i), {
key: "ArrowRight",
code: "ArrowRight",
keyCode: 39,
charCode: 39
});
const actions = store.getActions();
const rightAction = actions.filter(obj => obj.type==='RIGHT')
expect(rightAction.length).toEqual(1);
});
it('Should call storeToken', () => {
// const { storeToken } = renderWithState(<App />, { initialState });
// screen.debug();
const res = App.storeToken()
console.log('red', res);
// expect(App.storeToken).toBeTruthy();
// let node = shallow(<App />);
// const getPhoneCompSpy = jest.spyOn(node.instance(), 'storeToken');
// expect(getPhoneCompSpy).toHaveBeenCalled();
});
similar error in menu.test.js
** Menu.test.js **
import { render, screen } from "#testing-library/react";
import { Provider } from "react-redux";
import { Router } from "react-router-dom"
import configureStore from 'redux-mock-store';
import { createHashHistory as createHistory } from 'history';
import Menu from "./Menu"
const mockStore = configureStore([]);
const initialState = {
login: {
access: "access123",
refresh: "refresh123"
},
menu: {},
}
const menu = {
data: [
{
id: 'home',
title: 'Home',
isTvFocus: true,
isActive: true
},
{
id: 'mylist',
title: 'My list',
isTvFocus: false,
isActive: false
},
{
id: 'search',
title: 'Search',
isTvFocus: false,
isActive: false
}
]
}
const store = mockStore(initialState);
export const renderWithState = (
ui,
{ initialState, ...renderOptions } = {}
'initialState' is already declared in the upper scope.Why is this an issue?
) => {
const history = createHistory();
const Wrapper = ({ children }) => (
<Provider store={store}><Router history={history}>{children}</Router></Provider>
);
return render(ui, { wrapper: Wrapper, ...renderOptions });
};
jest.mock("teplay-ui/Components/Collection/TvMenu", () => {
return {
TvMenu: () => {
return <div data-testid="tvMenu">Home</div>;
}
};
});
test("renders Menu component with initial state", () => {
const { getByText, getByTestId } = renderWithState(<Menu />, { initialState });
screen.debug();
expect(getByText(/Home/i).textContent).toBe("Home");
expect(getByTestId(/tvMenu/)).toBeInTheDocument();
});
test("renders Menu component with menu props", () => {
initialState.menu = menu
const { getByText, getByTestId } = renderWithState(<Menu />, { initialState });
screen.debug();
expect(getByText(/Home/i).textContent).toBe("Home");
expect(getByTestId(/tvMenu/)).toBeInTheDocument();
});
what could possibly be the reason ?
For the last 2 hours, I've read unnumerous posts from StackOverflow, medium, and other independent blogs, and I haven't been able to crack or decipher how to properly mock a simple custom useAuth() hook.
I'm getting:
[TypeError: Cannot destructure property 'user' of '(0 , _auth.useAuth)(...)' as it is undefined.]
Here's my code:
The <Dashboard/> component which includes the useAuth() hook. (Code omitted due to brevity)
import { useAuth } from '../../../auth';
export const Dashboard: React.FC<RouteComponentProps> = (props) => {
const { user } = useAuth();
The dashboard.test.tesx file.
import { render, waitFor } from "../../../__tests_setup__/app-test-utils";
// Sets Up useAuth
import { mockPatientDetails } from "../../../setupTestPatient";
import { mockBaseAuth } from "../../../setupTestShared";
import { Dashboard } from "./index";
import { useAuth } from "../../../auth";
jest.mock("../../../auth");
describe("Tests the Patient's dashboard", () => {
beforeAll(() => {
(useAuth as any).mockReturnValue({
...mockBaseAuth,
user: {
...mockBaseAuth.user,
profile: mockPatientDetails.profile,
profile_id: mockPatientDetails.profile_id,
username: mockPatientDetails.username,
},
});
});
it("Will be able to assign a consultation now", async () => {
const renderer = await render(<Dashboard />);
waitFor(async () => {
await expect(renderer.getByText(`Hola ${mockPatientDetails.username}`));
});
expect(true).toBe(true);
});
});
Other variations tried:
import { render, waitFor } from "../../../__tests_setup__/app-test-utils";
// Sets Up useAuth
import { mockPatientDetails } from "../../../setupTestPatient";
import { mockBaseAuth } from "../../../setupTestShared";
import { Dashboard } from "./index";
import { useAuth } from "../../../auth";
// Variation 1
import * as auth from '../../../auth'
jest.spyOn(auth, "useAuth").mockImplementation(() => ({
...mockBaseAuth,
user: {
...mockBaseAuth.user,
profile: mockPatientDetails.profile,
profile_id: mockPatientDetails.profile_id,
username: mockPatientDetails.username,
},
}));
// Variation 2
jest.mock("../../../auth/index", () => ({
__esModule: true,
useAuth: jest.fn(() => ({
...mockBaseAuth,
user: {
...mockBaseAuth.user,
profile: mockPatientDetails.profile,
profile_id: mockPatientDetails.profile_id,
username: mockPatientDetails.username,
},
})),
}));
// Variation 3
There have been many other variations which I haven't included as I've completely forgotten about them.
Here's my folder structure, just in case.
P.S: Here are the variables shown above:
src/setupTestShared.ts
import { GENDER, InteractionMedias, UserProfile } from "./#dts";
import { useAuth } from "./auth";
const success = Promise.resolve({
type: "success" as const,
result: true,
});
export const mockBaseAuth: ReturnType<typeof useAuth> = {
login(u: string, p: string) {
return success;
},
authenticated: true,
logout() {},
register(p: UserProfile, pass: string) {
return success;
},
userExists(u: string) {
return Promise.resolve("USER_REGISTERED" as const);
},
user: {
date_of_birth: "1990-12-21",
email: "test#gmail.com",
emails: ["test#gmail.com"],
first_name: "Maria",
gender: GENDER.FEMALE,
id: 1,
identification_id: "402-2066666-1",
interaction_media_preferred: InteractionMedias.VIDEO,
last_name: "Anabelle",
loggedInDate: new Date(),
phones: ["809-544-5111"],
profile: "ANONYMOUS",
profile_id: 1,
username: "anonymous",
},
};
export const mockPatientDetails = {
username: "PAC123456",
profile: "patient" as const,
profile_id: 2,
};
What could it be?
It's working now!
This answer helped me:
https://stackoverflow.com/a/60282832/1057052
// https://stackoverflow.com/a/60282832/1057052
jest.mock("../../../auth", () => ({
// this isn't needed - __esModule: true,
useAuth: () => ({
...mockBaseAuth,
user: {
...mockBaseAuth.user,
profile: mockPatientDetails.profile,
profile_id: mockPatientDetails.profile_id,
username: mockPatientDetails.username,
},
}),
}));
The trick was not assigning the jest.fn() to the useAuth().
I try to test the following code
import { useSnackbar, VariantType, WithSnackbarProps } from 'notistack';
import React from 'react';
interface IProps {
setUseSnackbarRef: (showSnackbar: WithSnackbarProps) => void;
}
const InnerSnackbarUtilsConfigurator: React.FC<IProps> = (props: IProps) => {
props.setUseSnackbarRef(useSnackbar());
return null;
};
let useSnackbarRef: WithSnackbarProps;
const setUseSnackbarRef = (useSnackbarRefProp: WithSnackbarProps) => {
useSnackbarRef = useSnackbarRefProp;
};
export const SnackbarUtilsConfigurator = () =>
<InnerSnackbarUtilsConfigurator setUseSnackbarRef={setUseSnackbarRef} />;
export default {
success(msg: string) {
this.toast(msg, 'success');
},
warning(msg: string) {
this.toast(msg, 'warning');
},
info(msg: string) {
this.toast(msg, 'info');
},
error(msg: string) {
this.toast(msg, 'error');
},
toast(msg: string, variant: VariantType = 'default') {
useSnackbarRef.enqueueSnackbar(msg, { variant });
},
};
if I mock the complete notistack object
import React from "react";
import {render as testingRender} from '#testing-library/react';
import { SnackbarProvider} from 'notistack';
import SnackbarUtils,{SnackbarUtilsConfigurator} from './SnackbarUtils';
jest.mock('notistack');
beforeEach(() =>{
});
it('DownloadDialog renders correctly - open=true', async () => {
const component = await testingRender(<SnackbarProvider maxSnack={3}><SnackbarUtilsConfigurator /></SnackbarProvider>);
SnackbarUtils.success("success");
});
I get an error message:
TypeError: Cannot read property 'enqueueSnackbar' of undefined
33 | },
34 | toast(msg: string, variant: VariantType = 'default') {
> 35 | useSnackbarRef.enqueueSnackbar(msg, { variant });
| ^
36 | },
37 | };
because useSnackbarRef.
How can i mock only the useSnackbar method of notitstack, so that i can check if enqueueSnackbar is called with the correct values?
This works for me. Place this outside all tests (top level scope):
const mockEnqueue = jest.fn();
jest.mock('notistack', () => ({
...jest.requireActual('notistack'),
useSnackbar: () => {
return {
enqueueSnackbar: mockEnqueue
};
}
}));
i am writing a test case for a component and it is using redux ,saga ,and selectors
The test case as follows
const initialState = {
fetching: false,
error: null,
StockProducts: [],
products: [],
filter: '5b438ae60599132cb8b64b67'
}
const mockStore = configureStore()
const store = mockStore(fromJS(initialState))
import Stock from '../Stock'
test('for onUpdate funcion', () => {
const props = {
setErrorPopUp: jest.fn(),
formSubmitAttempt: jest.fn()
}
const wrapper = shallow(<Stock {...props} store={store} />)
wrapper.instance().onUpdate({ quantity: 0 })
expect(props.setErrorPopUp).toHaveBeenCalled()
wrapper.instance().onUpdate({ quantity: 3 })
expect(props.formSubmitAttempt).toHaveBeenCalled()
})
However test failed and i am getting this error
TypeError: Cannot read property 'fetching' of undefined
12 | createSelector(
13 | selectStockDomain,
> 14 | ({ fetching }) => fetching
15 | )
16 |
17 | const StockError = () =>
That is from one of the selectors i have used for the component
What is the wrong with my test and why is this happening
And how can i resolved this
this is the stock component
// react core
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'
// component imports
// Selcetors and actions
import StockActions from './actions'
import ToastActions from 'containers/Toasts/actions'
import {
StockFetching,
StockError,
StockProducts,
StockFilter,
Products,
userData
} from './selectors'
// partials
const mapStateToProps = createStructuredSelector({
fetching: StockFetching(),
error: StockError(),
stockProducts: StockProducts(),
products: Products(),
filter: StockFilter(),
userData: userData()
})
const mapDispatchToProps = dispatch => ({
getStockProducts: () => {
dispatch(StockActions.getStockAttempt())
},
getProducts: payload => {
dispatch(StockActions.getProductsAttempt(payload))
},
formSubmitAttempt: payload => {
dispatch(StockActions.formSubmitAttempt(payload))
},
deleteStock: payload => {
dispatch(StockActions.deleteStockAttempt(payload))
},
setErrorPopUp: payload => {
dispatch(
ToastActions.setToast(payload.message, payload.action, payload.time)
)
},
reset: () => {
dispatch(StockActions.reset())
}
})
class Stock extends Component {
state = {
openCard: false,
showCard: false,
confirmDelete: false
}
// to show product details
onCard = openCard => {
this.setState({
openCard
})
}
// to show and close add to stock option
_ShowCard = () => {
this.setState({
showCard: !this.state.showCard
})
}
// onDlete Stock
onDeleteStock = data => {
let {
props: { deleteStock },
state: { confirmDelete }
} = this
if (confirmDelete) {
deleteStock(data)
this.setState({
confirmDelete: false
})
} else {
this.setState({
confirmDelete: true
})
}
}
// update the products
handlesubmit = data => {
if (parseInt(data.quantity) < 1) {
this.props.setErrorPopUp({
message:
'You cannot set quantity to zero.Please enter valid qunatity and try again',
action: 'danger',
time: '5000'
})
} else {
this.props.formSubmitAttempt({
product: data.product.key,
quantity: data.quantity
})
this._ShowCard()
}
}
onUpdate = data => {
if (parseInt(data.quantity) < 1) {
this.props.setErrorPopUp({
message: 'You cannot set quantity to zero.Use delete actions',
action: 'danger',
time: '5000'
})
} else {
this.props.formSubmitAttempt({
product: data.id,
quantity: data.quantity
})
}
}
onCancelDelete = () => {
this.setState({
confirmDelete: false
})
}
onGetStock = () => {
let { getStockProducts, getProducts, userData } = this.props
getStockProducts()
getProducts({ user: userData._id })
}
componentDidMount () {
this.onGetStock()
}
render () {
let {
props: { stockProducts, error, products, fetching },
state: { openCard, showCard, confirmDelete },
onCard,
onGetStock,
_ShowCard,
handlesubmit,
onUpdate,
onDeleteStock,
onCancelDelete
} = this
return (
<KeyboardAvoidingWrapper fluid enabled={!showCard}>
UI GOES HERE ....
</KeyboardAvoidingWrapper>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Stock)
Thanks
When you are testing a Component, it's a good idea to test just the behaviour of the Component independent of your redux store. Because, internally the store passes in state to the Component as props.
1. export your Component
So your Component definition code becomes
class Stock extends Component {
to
export class Stock extends Component {
2. import just the Component without the Connect HOC
Like this
import { Stock } from '../Stock'
test('for onUpdate funcion', () => {
const props = {
setErrorPopUp: jest.fn(),
formSubmitAttempt: jest.fn()
}
const wrapper = shallow(<Stock {...props} />)
// modify your code to see how your component behaves with different props
wrapper.instance().onUpdate({ quantity: 0 })
expect(props.setErrorPopUp).toHaveBeenCalled()
wrapper.instance().onUpdate({ quantity: 3 })
expect(props.formSubmitAttempt).toHaveBeenCalled()
})
I am trying to test my async Redux action which gets data from Firebase.
I use jest and moxios to mock the async call
actionTypes.js
export const FETCH_ORDERS_START = 'FETCH_ORDERS_START'
export const FETCH_ORDERS_SUCCESS = 'FETCH_ORDERS_SUCCESS'
export const FETCH_ORDERS_FAILED = 'FETCH_ORDERS_FAILED'
order.js
import * as actionTypes from './actionTypes'
import axios from './../../axios-orders'
export const fetchOrdersSuccess = (orders) => {
return {
type: actionTypes.FETCH_ORDERS_SUCCESS,
orders: orders,
}
}
export const fetchOrdersFailed = (error) => {
return {
type: actionTypes.FETCH_ORDERS_FAILED,
error: error,
}
}
export const fetchOrdersStart = () => {
return {
type: actionTypes.FETCH_ORDERS_START,
}
}
export const fetchOrders = (token, userId) => {
return dispatch => {
dispatch(fetchOrdersStart())
const queryParams = `?auth=${token}&orderBy="userId"&equalTo="${userId}"`
axios.get('/orders.json' + queryParams)
.then(resp => {
const fetchedData = []
for (let key in resp.data) {
fetchedData.push({
...resp.data[key],
id: key,
})
}
dispatch(fetchOrdersSuccess(fetchedData))
})
.catch( error => dispatch(fetchOrdersFailed(error)))
}
}
In my test, i expect that calling fetchOrders(token, userId) will produce two redux actions: START and SUCCESS
import moxios from 'moxios';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import * as actionTypes from './actionTypes';
import * as actions from './order'
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const ordersMock = {
"-LGyxbZUSr5Q4jboj0uw" : {
"ingredients" : {
"bacon" : 0,
"cheese" : 0,
"meat" : 1,
"salad" : 0
},
}
}
describe('order actions', () => {
beforeEach(function () {
moxios.install();
});
afterEach(function () {
moxios.uninstall();
});
it('creates FETCH_ORDER_SUCCESS after successfuly fetching orders', () => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: ordersMock,
});
});
const expectedActions = [
{ type: actionTypes.FETCH_ORDERS_START },
{ type: actionTypes.FETCH_ORDERS_SUCCESS, orders: ordersMock },
];
const store = mockStore({ posts: {} })
store.dispatch(actions.fetchOrders("TOKEN", "USER_ID"))
console.log(store.getActions())
expect(store.getActions()).toEqual(expectedActions);
})
})
Unfortunately it always seems to create SUCCESS and FAILED actions. How to properly mock axios call with queryParameters.
In fetchOrders is use my own axios instance with set base-name:
import axios from 'axios'
const instance = axios.create({
baseURL: 'https://urltofirebase.com'
})
export default instance
Here is the solution only use jestjs and typescript, without maxios module.
order.ts:
import * as actionTypes from './actionTypes';
import axios from 'axios';
export const fetchOrdersSuccess = orders => {
return {
type: actionTypes.FETCH_ORDERS_SUCCESS,
orders
};
};
export const fetchOrdersFailed = error => {
return {
type: actionTypes.FETCH_ORDERS_FAILED,
error
};
};
export const fetchOrdersStart = () => {
return {
type: actionTypes.FETCH_ORDERS_START
};
};
export const fetchOrders = (token, userId) => {
return dispatch => {
dispatch(fetchOrdersStart());
const queryParams = `?auth=${token}&orderBy="userId"&equalTo="${userId}"`;
return axios
.get('/orders.json' + queryParams)
.then(resp => {
dispatch(fetchOrdersSuccess(resp));
})
.catch(error => dispatch(fetchOrdersFailed(error)));
};
};
order.spec.ts:
import thunk, { ThunkDispatch } from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import * as actionTypes from './actionTypes';
import * as actions from './order';
import { AnyAction } from 'redux';
import axios from 'axios';
type State = any;
const middlewares = [thunk];
const mockStore = configureMockStore<State, ThunkDispatch<State, undefined, AnyAction>>(middlewares);
const ordersMock = {
'-LGyxbZUSr5Q4jboj0uw': {
ingredients: {
bacon: 0,
cheese: 0,
meat: 1,
salad: 0
}
}
};
describe('order actions', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('creates FETCH_ORDER_SUCCESS after successfuly fetching orders', () => {
expect.assertions(2);
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce(ordersMock);
const expectedActions = [
{ type: actionTypes.FETCH_ORDERS_START },
{ type: actionTypes.FETCH_ORDERS_SUCCESS, orders: ordersMock }
];
const store = mockStore({ posts: {} });
return store.dispatch(actions.fetchOrders('TOKEN', 'USER_ID')).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(getSpy).toBeCalledWith('/orders.json?auth=TOKEN&orderBy="userId"&equalTo="USER_ID"');
});
});
});
Unit test result with coverage report:
PASS src/stackoverflow/51983850/order.spec.ts (7.79s)
order actions
✓ creates FETCH_ORDER_SUCCESS after successfuly fetching orders (8ms)
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 88.89 | 100 | 71.43 | 88.89 | |
actionTypes.ts | 100 | 100 | 100 | 100 | |
order.ts | 86.67 | 100 | 71.43 | 86.67 | 12,32 |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.753s, estimated 19s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/51983850