简体   繁体   English

为我的每个 React 组件添加了一个导航栏,我如何在测试中模拟导航栏,因为所有测试都失败,因为导航栏组件中的方法调用是 null?

[英]Added a navbar to each of my React components, how do I mock the navbar in test as all tests failing as a method call in Navbar component is null?

I am working on a project and was implementing tests for each component that I implemented.我正在开发一个项目,并且正在为我实现的每个组件实施测试。 These tests were all passing but now fail because I have since added a AppNavbar component that makes a call to the AuthService to get the currently logged in user's id to create a dynamic Route link to profile for that user.这些测试都通过了,但现在失败了,因为我已经添加了一个 AppNavbar 组件,该组件调用 AuthService 以获取当前登录用户的 id,从而为该用户创建一个动态路由链接到配置文件。

I tried mocking the AppNavbar using mockComponent(AppNavbar);我尝试了 mocking 使用 mockComponent(AppNavbar) 的 AppNavbar; but it gives me error:但它给了我错误:

Cannot read properties of undefined (reading 'render')

In my tests, this value is obviously null because no user is logged in. How can I mock away the navbar so it doesn't interfere with the tests?在我的测试中,这个值显然是 null 因为没有用户登录。我怎样才能模拟导航栏,这样它就不会干扰测试? Or mock the value of the AuthService?或者模拟 AuthService 的值?

//LoginForm.test.js: //LoginForm.test.js:

const server = setupServer(
rest.post(config.URL + "/auth/login", (req, res, ctx) => {
    return res(ctx.json("Success."))
}),
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

const renderWithRouter = (element) => (
    render(
        <MemoryRouter initialEntries={['/sign-in']}>
            <Routes>
                <Route path='/sign-in' element={element} />
            </Routes>
        </MemoryRouter>
    )
);

describe("loginForm", () => {
    it("should render the fields", () => {
        mockComponent(AppNavbar);
        renderWithRouter(<LoginForm />);
        expect(
            screen.getByRole("heading", { name: "Login" })
        ).toBeInTheDocument();
        expect(screen.getByRole("textbox", { name: "Email Address:" })).toBeInTheDocument();
        expect(
            screen.getByRole("textbox", { name: "Password:" })
        ).toBeInTheDocument();
        expect(screen.getByRole("button", { name: "Submit" })).toBeInTheDocument();
    });
    it("should validate form fields", async () => {
        mockComponent(AppNavbar);
        renderWithRouter(<LoginForm />);
        fireEvent.input(screen.getByRole("textbox", { name: "Email Address:" }), {
            target: {
                value:
                    "jojeaojoeaj@outlook.com"
            }
        });
        fireEvent.input(screen.getByRole("textbox", { name: "Password:" }), {
            target: {
                value:
                    "password-1"
            }
        });

        fireEvent.submit(screen.getByRole("button", { name: "Submit"}));
        console.log(await screen.findAllByRole("alert"));
        expect(await screen.findAllByRole("alert")).toHaveLength(1);
    });
});

//LoginForm.js: //登录表单.js:

   const defaultValues = {
    //The email to attempt to login with.
    email: "",
    //The password to attempt to login with.
    password: "",
};

export default function LoginForm() {

    const [values, setValues] = useState(defaultValues);
    const [alert, setAlert] = useState(false);
    const [alertMessage, setAlertMessage] = useState('');
    const [alertSeverity, setAlertSeverity] = useState('');
    const [isLoading, setLoading] = useState(false);
    const navigate = useNavigate();

    const handleChange = (e) => {
        const { name, value } = e.target;
        setValues({
            ...values,
            [name]: value,
        });
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        setLoading(true);
        if (validateInput(values.email, values.password).includes("Failure: Email")) {
            setLoading(false);
            setAlertMessage("Login failed, please enter a valid email, emails must be between 4 and 50 characters.");
            setAlertSeverity("error");
            setAlert(true);
            return;
        }
        if (validateInput(values.email, values.password).includes("Failure: Password")) {
            setLoading(false);
            setAlertMessage("Login failed, please enter a valid password, passwords must be between 6 and 12 characters.");
            setAlertSeverity("error");
            setAlert(true);
            return;
        }
        const payload = {
            "email": values.email,
            "password": values.password,
        };

        setLoading(false);
        AuthService.loginUser(payload).then(response => response.json())
            .then(async data => {
                    console.log(data);
                    if (data.userId && data.accessToken) {
                        setAlertMessage("Login successful");
                        setAlertSeverity("success");
                        setAlert(true);

                        const authenticatedUser = {
                            "userId": data.userId,
                            "accessToken": data.accessToken,
                        }
                        localStorage.setItem('authenticatedUser', JSON.stringify(authenticatedUser));

                        await delay(1000);
                        navigate('/');
                    }
                }
            );
        setAlertMessage("Login failed, probably because no user exists with these credentials.");
        setAlertSeverity("error");
        setAlert(true);
    }

    if (isLoading) {
        return (
            <div id="loading">
                <h2>Loading...</h2>
            </div>
        )
    }

    const validateInput = (email, password) => {
        if (email.length > 50) {
            return "Failure: Email entered is too long."
        }
        if (email.length < 4) {
            return "Failure: Email is invalid."
        }
        if (password.length > 12) {
            return "Failure: Password is too long."
        }
        if (password.length < 6) {
            return "Failure: Password is too short."
        } else {
            return "Success."
        }
    }


    return (
        <>
            <AppNavbar />
            <div id="loginFormContainer">
                {alert ? <Alert severity={alertSeverity} role="alert">{alertMessage}</Alert> : <></> }
                <div id="loginFormDiv">
                    <h5 id="loginFormHeading">Login</h5>
                    <br />
                    <form onSubmit={handleSubmit}>
                        <label className="formLabel">
                            <br />
                            Email Address:
                            <br />
                            <input
                                className="formInput"
                                value={values.email}
                                onChange={handleChange}
                                name="email"
                                type="email"
                            />
                        </label>
                        <label className="formLabel">
                            <br />
                            Password:
                            <br />
                            <input
                                className="formInput"
                                value={values.password}
                                onChange={handleChange}
                                name="password"
                            />
                        </label>
                        <br />
                        <button id="form-submit" type="submit" name="submit">Submit</button>
                    </form>
                </div>
            </div>
        </>
    );
}

//AppNavbar.js //AppNavbar.js

export const AppNavbar = () => {

const userId = AuthService.getCurrentlyAuthenticatedUser().userId;

const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);

return (
    <>
        <Navbar className="NavBar" color="dark" dark expand="sm">
            <NavbarBrand id="NavBarBrand" href="/">Po</NavbarBrand>
            <NavbarToggler onClick={toggle} />
            <Collapse isOpen={isOpen} navbar>
                <Nav navbar className="ml-auto" id="Nav">
                    <NavItem className="NavLink">
                        <NavLink href={`/profile/${userId}`} className="NavLink">Profile</NavLink>
                    </NavItem>
                    <UncontrolledDropdown nav inNavbar>
                        <DropdownToggle nav caret >
                            Play
                        </DropdownToggle>
                        <DropdownMenu end>
                            <DropdownItem tag="a" href={'/players'}>
                                Players
                            </DropdownItem>
                            <DropdownItem tag="a" href={'/top-players'}>
                                Top Players
                            </DropdownItem>
                        </DropdownMenu>
                    </UncontrolledDropdown>
                    <UncontrolledDropdown nav inNavbar>
                        <DropdownToggle nav caret>
                            Games
                        </DropdownToggle>
                        <DropdownMenu end>
                            <DropdownItem tag="a" href={'/record-game'}>
                                Record Game
                            </DropdownItem>
                            <DropdownItem tag="a" href={'/game-history'}>
                                Game History
                            </DropdownItem>
                        </DropdownMenu>
                    </UncontrolledDropdown>
                </Nav>
            </Collapse>
        </Navbar>
    </>
)

} }

export default AppNavbar;导出默认 AppNavbar;

//AuthService.js: //AuthService.js:

getCurrentlyAuthenticatedUser() {
    return JSON.parse(localStorage.getItem("authenticatedUser"));
};

The tests are implemented using Jest/React-testing-library.这些测试是使用 Jest/React-testing-library 实现的。

Thanks for any help!谢谢你的帮助!

Option 1选项1

If the AppNavBar is on every page, you should create a container which renders the AppNavBar alongside any arbitrary page content.如果AppNavBar在每个页面上,您应该创建一个容器,将AppNavBar与任意页面内容一起呈现。 This will be much simpler to test, as you will be able to test each page in isolation from the AppNavBar .这将更容易测试,因为您将能够独立于AppNavBar测试每个页面。

const Page = ({ children }) => {
    return <>
        <AppNavBar />
        {children}
    </>
}

This approach may also save some future headaches, as it's more consistent with DRY principles .这种方法也可以避免一些未来的麻烦,因为它更符合DRY 原则

Option 2选项 2

If Option 1 doesn't suit your use case, you should just mock the whole component.如果选项 1 不适合您的用例,您应该模拟整个组件。

// replace '../AppNavBar' with actual location of component
jest.mock('../AppNavBar', () => <>MOCK APP NAV</>);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在React中滚动时,如何使导航栏突出显示? - How do I make my navbar highlight as I scroll in React? 如何将不同的新闻组件呈现到导航栏组件中并在导航栏页面上显示它们的数据,点击导航栏菜单链接 - How can I render different news components into the Navbar Component & display their data on the Navbar page, On Click of Navbar menu links 如何使底部导航栏粘贴到顶部导航栏? - How do I make my bottom navbar stick to my top navbar? 如何将我的登录组件集成到导航栏中?(附代码) - How Can I integrate my Login Component in Navbar?(Code Attached) 如何从响应式导航栏使我的导航栏静态化? - How can I make my navbar static from a responsive navbar? 我如何更新 api 电话上的导航栏(特别是谷歌登录) - how do i update a navbar on an api call (specifically a google signin) React:如何更改导航栏的颜色? - React: How can I change the color of my navbar? 我怎么能把我的背景图片放在导航栏后面做出反应? - How could I put my background image behind a Navbar in react? 如何在 JS 中使用 React Boostrap 创建 NavBar? - How do I create a NavBar using React Boostrap in JS? 如何在 React/MUI 导航栏中制作单独的菜单/菜单项? - How do I make separate Menu/MenuItem in a React/MUI navbar?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM