I'm pretty new to testing
and started to work with react testing library
.
The problem I've, is that fireEvent
doesn't trigger, and the error throws, is following:
Expected number of calls: >= 1
Received number of calls: 0
So, I'd highly appreciate, if I could get answer for my questions:
For this particular test do I need to work with @testing-library/react-hooks
?
Do I need to render my Product component wrapped on context Provider
where the function is imported from?
I tried to reproduce the code and leave relevant code.
Product.tsx
component
import React, { FunctionComponent } from "react";
import { useProducts } from "./ContextWrapper";
import { Button } from "@mui/material";
import { articleProps } from "./types";
const Product: FunctionComponent<{ article: articleProps }> = ({
article,
}) => {
const { handleAddToCart } = useProducts(); // this is from global context
return (
<Button
aria-label="AddToCart"
onClick={() => handleAddToCart(article)}
>
Add to cart
</Button>
);
};
export default Product;
Product.test.tsx
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import Product from "./Product";
import { act, renderHook } from "@testing-library/react-hooks";
const article = {
name: "samsung",
variantName: "phone",
categories: [],
};
const renderProduct = () => {
return render(
<ContextProvider>
<Product article={article} />
</ContextProvider>
);
};
//first test just tests if button exists with provided content and works fine
test("renders product", () => {
render(<Product article={article} />);
const AddToCartbutton = screen.getByRole("button", { name: /AddToCart/i });
expect(AddToCartbutton).toHaveTextContent("Add to cart");
});
// this test throws the error described above
test("test addToCart button", () => {
renderProduct();
const onAddToCart = jest.fn();
const AddToCartbutton = screen.getByRole("button", { name: /AddToCart/i });
fireEvent.click(AddToCartbutton);
expect(onAddToCart).toHaveBeenCalled();
});
ContextWrapper.tsx
import React, {createContext, useContext, ReactNode, useState} from "react";
import { prodProps } from "../types";
type ProductContextProps = {
productData: prodProps;
handleAddToCart: (clickedItem: productPropsWithAmount) => void;
};
const ProductContextDefaultValues: ProductContextProps = {
productData: null as any,
handleAddToCart:null as any;
};
type Props = {
children: ReactNode;
};
const ProductContext = createContext<ProductContextProps>(
ProductContextDefaultValues
);
export function useProducts() {
return useContext(ProductContext);
}
const ContextWrapper = ({ children }: Props) => {
const { data } = useGraphQlData();
const [productData, setProductData] = useState<Category>(data);
const [cartItems, setCartItems] = useState<prodProps[]>([]);
useEffect(() => {
if (data) {
setProductData(data);
}
}, [data]);
const handleAddToCart = (clickedItem: prodProps) => {
setCartItems((prev: prodProps[]) => {
return [...prev, { ...clickedItem }];
});
};
return (
<ProductContext.Provider
value={{
productData,
handleAddToCart,
}}
>
{children}
</ProductContext.Provider>
);
};
export default ContextWrapper;
Thanks in advance
CodePudding user response:
I advise you to create Button Component and test it separately like this:
const onClickButton = jest.fn();
await render(<Button onClick={onClickButton} />);
const AddToCartbutton = screen.getByRole("button", { name: /AddToCart/i });
await fireEvent.click(AddToCartbutton);
expect(onClickButton).toHaveBeenCalled();
CodePudding user response:
One way to accomplish this is to mock ContextWrapper
, as your test is specifically referring to Product
component.
So, you could do something like this into your test:
import * as ContextWrapper from '--- PATH TO ContextWrapper ---';
test('test addToCart button', () => {
/// mockFunction
const onAddToCart = jest.fn();
jest.spyOn(ContextWrapper, 'useProducts').mockImplementationOnce(() => ({
productData: {
example_prodProp1: 'initialValue1',
example_prodProp2: 'initialValue2',
},
// Set mockFunction to handleAddToCart of useProducts
handleAddToCart: onAddToCart,
}));
render(<Product article={article} />);
const AddToCartbutton = screen.getByRole('button', { name: /AddToCart/i });
fireEvent.click(AddToCartbutton);
expect(onAddToCart).toHaveBeenCalledWith({
prodProp1: 'samsung',
prodProp2: 'phone',
});
});
In this line jest.spyOn(ContextWrapper, 'useProducts').mockImplementation
we are mocking useProducts
return value and setting the handleAddToCart
function to your mockFunction and that's how you can check if it has been called.
* This test is strictly focused on Product
component and you just want to garantee that the component calls the handleAddToCart
function from ContextWrapper
.
For test how handleAddToCart
should work, you can create a specific test for the ContextWrapper
.