Home > OS >  How to test this component staying aligned with React Testing Library Principles?
How to test this component staying aligned with React Testing Library Principles?

Time:07-07

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.

  • Related