Hi, I'm new to unit testing, I have a component that renders a list of products and displays those lists. I also have a function in this component to filter these products by price range. I'm using ReactSlider which basically updates two state variables "priceFrom" and "priceTo" and when these state variables change I trigger a function that filters the products based on those prices. So I wanted to test a similar functionality in jest. Is it possible to joke? If so, how can I achieve this, and if not, what alternative can I take to accomplish unit testing of this component. Thanks. The code for this component is as follows. Note that in the code below, when theReactSliderupdates any value, thefilterItemfunction filters the products and updates the ui.
import React, { useEffect, useState, useContext } from "react"; import { useParams } from "react-router-dom"; import styles from './productsList.module.css'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import Backdrop from "../../Others/Backdrop/backdrop"; import Modal from '../../Others/Modal/modal'; import ReactPaginate from 'react-paginate'; import ReactSlider from 'react-slider'; import { disableScroll } from "../../Others/HelperFunction/helperFunction"; import { ContextProvider } from "../../Others/AuthContext/authContext"; import './slider.css'; const ProductsList = () => { const context = useContext(ContextProvider); const productId = useParams().productId; const [sidebar, setSidebar] = useState(false); const [backdrop, setBackdrop] = useState(false); const [products, setProducts] = useState([]); const [filteredProducts, setFilteredProducts] = useState([]); const [error, setError] = useState(false); const [status, setStatus] = useState(''); const [modal, setModal] = useState(false); const [priceFrom, setPriceFrom] = useState(0); const [priceTo, setPriceTo] = useState(10000); const [itemOffset, setItemOffset] = useState(0); const [itemNotFound, setItemNotFound] = useState(false); const itemPerPage = 9; const endOffset = itemOffset + itemPerPage; let pageCount = 0; if (filteredProducts.length){ pageCount = Math.ceil(filteredProducts.length / itemPerPage); } else { pageCount = Math.ceil(products.length / itemPerPage); } const handlePageClick = (event) => { if (filteredProducts.length){ const newOffset = (event.selected * itemPerPage) % filteredProducts.length; setItemOffset(newOffset); } else { const newOffset = (event.selected * itemPerPage) % products.length; setItemOffset(newOffset); } } useEffect(() => { window.scrollTo(0, 0); if (context.data !== undefined){ const product = context.data[productId] !== undefined ? context.data[productId] : []; if (product.length){ setProducts(product); setStatus('success'); } else { setStatus('not found'); } } }, [context.data] ); useEffect(() => { window.scrollTo(0, 0); }, [itemOffset, filteredProducts.length]); useEffect(() => { if (backdrop){ disableScroll(); } else { window.onscroll = () => { } } }, [backdrop]) let defaultView = Array.from(Array(12).keys()).map(item => { return}); if (products.length){ if (!itemNotFound && filteredProducts.length){ defaultView = filteredProducts.slice(itemOffset, endOffset).map(item => { return }); } else if (itemNotFound) { defaultView = } else { defaultView = products.slice(itemOffset, endOffset).map(item => { return }); } } else if(status === 'not found') { defaultView =Nothing found based on your range
} const filterItem = () => { if (priceFrom && priceTo) { const filteredData = products.filter(item => Number(item.price) >= priceFrom && Number(item.price) <= priceTo); if (filteredData.length){ setItemNotFound(false) setFilteredProducts(filteredData); setSidebar(false); setBackdrop(false); } else { setItemNotFound(true); setItemOffset(0); setSidebar(false); setBackdrop(false); } } } const resetFilter = () => { setPriceFrom(0); setPriceTo(1000); setFilteredProducts([]); } const openSidebar = () => { if (!sidebar){ setSidebar(true); setBackdrop(true); } } const closeSidebar = () => { if (sidebar){ setSidebar(false); setBackdrop(false); } else { setBackdrop(false); } } let displayStatus =Nothing found
if (status === 'database error'){ displayStatus =Something went wrong
Please try again
} return ( <>Database error
Please try again or contact the admin
{displayStatus} > ) } export default ProductsList;Show Sidebar
Price Range
`Thumb value ${state.valueNow}`} renderThumb={(props, state) => {state.valueNow}} minDistance={1} onChange={([v1, v2]) => { setPriceFrom(v1); setPriceTo(v2); }} pearling />{products.length ? products[0].category : null}
{defaultView}
I can provide you with some suggestions related to your problem
Check out a library that can help you test React elements (such as react-testing-libraryhttps://testing-library.com/docs/react-testing-library/intro/)
Usually something from another library won't be tested because we assume the library itself has tested it (in this case the ReactSlider library
Try to split the component into smaller components so it is easier to test (you can use props) and there will be fewer dependencies inside each component
We can divide the tests here into two different testsE2EandUnit test, E2E is when you use a tool that simulates user interaction, here In this case you can simulate the user moving the range slider and see what happens afterwards (for this you can use cypress or selenium) These tools run like a human in the browser and unit tests allow you to test functionality, component inputs and Output, here you can also simulate clicks and interactions, but they are not as powerful as E2E for user interaction, because the unit tests (in this case the joke tool) run the tests in JSDOM, which is a simulation of the real browser (simply put)
To fix this I will do the following
Cypress:https://www.cypress.io/
Selenium:https://www.selenium.dev/
Keep in mind that E2E and unit testing are the two approaches that will serve you best depending on what you are looking for, each approach simulates user interaction but has a different engine behind it
Hope it helps you