如何测试该组件与React测试库原理保持一致?

发布于 2025-02-13 10:42:50 字数 4605 浏览 0 评论 0原文

我正在测试一个组件,并想知道最好的方法是在没有测试实现细节的情况下测试组件

该组件根据widthisDropdown状态有条件地渲染,但不确定如何通过更改这些值来测试DOM的外观,甚至是正确的方法。

这是一个组件:

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

这是我的初始测试,它仅在文本正确出现在DOM中的情况下才能测试,它确实可以:

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()
})

谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

╄→承喏 2025-02-20 10:42:50

我会为feed的每组道具编写测试,一个测试嘲笑setFeedChoice props props并断言其呼叫:

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()
});

因此,您正在登台feed 组件,您可以测试它仅在输入(Prop)和输出(DOM生成,下拉开放,文本,回调调用)方面的行为。您不仅测试内部实现,而只能测试进出的内容。

困难的部分可能是与下拉菜单正确交互并使其正确打开/关闭。您可能必须使用waitfor,或者 by fireevent (根据我的经验,有时fireeevent更可靠 - 但不太现实)。 See React Testing Library: When使用userevent.click并何时在此主题上使用fireevent

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文