I am testing a component and wondering what the best approach would be that would test the component without testing implementation details.
The component renders conditionally based on the width
and isDropdown
state, but not sure how to test how the DOM would look by changing these values or even if that would be the correct approach.
Here is the component:
import { useState } from 'react'
import useWindowDimension from '../../hooks/useWindowDimensions'
import { QueryType } from '../../constants'
type choiceProps = {
type: QueryType
isChosen: boolean
setFeedChoice: (query: QueryType) => void
}
const FeedChoice = ({ type, isChosen, setFeedChoice }: choiceProps) => {
return (
<div
className={isChosen ? 'feed-choice chosen' : 'feed-choice'}
onClick={() => setFeedChoice(type)}
>
<img
src={require(`../../../public/images/${type}${
isChosen ? '-fill' : ''
}.svg`)}
/>
<span>{type.charAt(0).toUpperCase() type.slice(1)}</span>
</div>
)
}
type Props = {
feedChoice: QueryType
setFeedChoice: (query: QueryType) => void
}
const Feed = ({ feedChoice, setFeedChoice }: Props) => {
const { width } = useWindowDimension()
const [isDropdown, setDropdown] = useState<boolean>(false)
const setChoice = (query: QueryType) => {
setFeedChoice(query)
setDropdown(false)
}
if (width !== null && width <= 600) {
return (
<div className="feed-row">
{isDropdown ? (
<div>
<FeedChoice
type={QueryType.New}
isChosen={feedChoice === QueryType.New}
setFeedChoice={setChoice}
/>
<FeedChoice
type={QueryType.Boost}
isChosen={feedChoice === QueryType.Boost}
setFeedChoice={setChoice}
/>
<FeedChoice
type={QueryType.Comments}
isChosen={feedChoice === QueryType.Comments}
setFeedChoice={setChoice}
/>
<FeedChoice
type={QueryType.Squash}
isChosen={feedChoice === QueryType.Squash}
setFeedChoice={setChoice}
/>
</div>
) : (
<FeedChoice
type={feedChoice}
isChosen={true}
setFeedChoice={() => setDropdown(true)}
/>
)}
</div>
)
} else {
return (
<div className="feed-row">
<FeedChoice
type={QueryType.New}
isChosen={feedChoice === QueryType.New}
setFeedChoice={setFeedChoice}
/>
<div className="divider"></div>
<FeedChoice
type={QueryType.Boost}
isChosen={feedChoice === QueryType.Boost}
setFeedChoice={setFeedChoice}
/>
<div className="divider"></div>
<FeedChoice
type={QueryType.Comments}
isChosen={feedChoice === QueryType.Comments}
setFeedChoice={setFeedChoice}
/>
<div className="divider"></div>
<FeedChoice
type={QueryType.Squash}
isChosen={feedChoice === QueryType.Squash}
setFeedChoice={setFeedChoice}
/>
</div>
)
}
}
export default Feed
Here is my initial test that is only testing if the text properly appears in the DOM, which it does:
import React from 'react'
import { screen, render } from '@testing-library/react'
import Feed from '../components/feed/feed'
import useWindowDimensions from '../hooks/useWindowDimensions'
test('should render all of the QueryTypes in the Feed', () => {
render(<Feed />)
expect(screen.getByText(/new/i)).toBeInTheDocument()
expect(screen.getByText(/boost/i)).toBeInTheDocument()
expect(screen.getByText(/comments/i)).toBeInTheDocument()
expect(screen.getByText(/squash/i)).toBeInTheDocument()
})
Thanks!
CodePudding user response:
I would write tests for each set of props for Feed
, and one test mocking the setFeedChoice
props and asserting on its calls :
const setFeedChoiceMock = jest.fn();
test('calls callback correctly', () => {
render(<Feed feedChoice=... setFeedChoice={setFeedChoiceMock} />)
// click on whatever makes the drop down open
userEvent.click(...);
// click on one option
userEvent.click(screen.getByText(...));
// check that the callback was called
expect(setFeedChoiceMock).toHaveBeenCalledWith("expected parameter, option text...");
});
// write other tests changing the feedChoice props to ensure all cases work well
test('respects feedChoice props', () => {
render(<Feed feedChoice="other value" setFeedChoice={setFeedChoiceMock} />)
// check that feedChoice is correctly used
expect(screen.getByText(...)).toBeInTheDocument()
});
Therefore you are staging the Feed
component, and you test how it behaves regarding only inputs (props) and outputs (DOM generated, dropdown opening, texts, callback calls). You don't test internal implementation, only what's in and out.
The difficult part may be to interact correctly with the dropdown and make it open/close correctly. You may have to use waitFor
, or replace userEvent
by fireEvent
(in my experience sometimes fireEvent
is more reliable - but less realistic). See React Testing Library: When to use userEvent.click and when to use fireEvent on this subject.