当我使用react-testing-library时,它说error:invariant expected app router to be mounted,在开发环境中运行时没有这样的问题。
测试逻辑在这里
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import NavBar from "@/components/NavBar";
describe("<NavBar>", () => {
it ("the login pop out displayed after click the login/sign up button", async () => {
render(<NavBar />);
const loginButton = screen.getByRole("link", {
name: "LOGIN/SIGN UP"
});
const loginCard = screen.getByTestId("loginCard");
await userEvent.click(loginButton);
expect(loginButton).toBeCalled();
expect(loginCard).toBeVisible();
})
});
字符串
组件在这里:导航栏:
"use client"
import React, { Dispatch, SetStateAction } from "react";
import { useRouter } from "next/navigation";
interface NavBarProps {
setVisibleLogin?: Dispatch<SetStateAction<boolean>>;
}
const NavBar = ({ setVisibleLogin }: NavBarProps) => {
const router = useRouter();
const handleLoginBtn = () => {
setVisibleLogin && setVisibleLogin(true);
};
const handleHome = () => {
router.push("/");
};
return (
<div className="bg-slate-50 flex justify-center">
<div className="fix w-912px top-0 navbar w-require">
<div className="navbar-start">
<a
className="btn btn-ghost normal-case text-xl text-sky-500"
onClick={handleHome}
>
Alimama MealGPT
</a>
</div>
<div className="navbar-center hidden lg:flex"></div>
{
// for distinguish between the page for login and others
setVisibleLogin && (
<div className="navbar-end">
<a className="btn" onClick={handleLoginBtn}>
Login/Sign Up
</a>
</div>
)
}
</div>
</div>
);
};
export default NavBar;
型
登录卡:
"use client"
import React, { Dispatch, SetStateAction } from "react";
import InputBox from "./InputBox";
import Image from "next/image";
import { useRouter } from "next/navigation";
import GoogleLoginCard from "@/assets/[email protected]"
interface LoginCardProps {
visibleLogin: boolean;
setVisibleLogin: Dispatch<SetStateAction<boolean>>;
}
const LoginCard = ({visibleLogin, setVisibleLogin}: LoginCardProps) => {
const router = useRouter();
const handleCancel = () => {
setVisibleLogin(false);
}
const handleSignUp = () => {
router.push("/SignUp");
}
return (
<div
className="fixed left-1/2 top-1/2 -translate-x-2/4 -translate-y-2/4 z-50"
style={{ visibility: visibleLogin ? "visible" : "hidden" }}>
<div className="card w-96 bg-neutral text-neutral-content bg-slate-200">
<div className="flex flex-row-reverse">
<div className="flex flex-row-reverse" style={
{
position: "relative",
top: "0.5rem",
right: "0.5rem"
}
}>
<button className="btn btn-outline w-2 h-2" onClick={handleCancel}>X</button>
</div>
<a className="relative right-10 top-3 btn btn-ghost normal-case text-xl text-sky-500">Alimama MealGPT</a>
</div>
<div className="card-body items-center text-center">
<h2 className="card-title">Login</h2>
<InputBox
title="Email/ User ID"
textHolder="Please Enter Your Email or ID Here"
otherLeftOption={false}
otherRightOption={false}
/>
<InputBox
title="Password"
textHolder="Please Enter Your Password Here"
otherLeftOption={true}
otherRightOption={true}
optionLeftText="Forget Password?"
optionRightText="Any Other Helps?"
/>
<div className="card-actions justify-end">
<button className="btn btn-primary w-27">Log In</button>
<button className="btn btn-primary w-25" onClick={handleSignUp}>Sign Up</button>
<button className="btn btn-outline" onClick={handleCancel}>Cancel</button>
</div>
<div>
<Image src={GoogleLoginCard}
height={200}
width={200}
alt="google login button"
/>
</div>
</div>
</div>
</div>
);
};
export default LoginCard;
型
我看到其他一些类似的问题有这样的解决方案,如添加HTML头和身体标记上的布局。tsx,我把头和身体标记在布局。tsx已经。
有人知道如何解决这个问题吗?
我期待解决问题的办法。
2条答案
按热度按时间kd3sttzy1#
对我来说,在Jest测试中模拟useRouter是这样的:
字符串
这个想法是从这里取来的(我只是把“next/router”改成了“next/navigation”)。
mzsu5hc02#
在一个真实的Next.js应用程序中你的组件树被包裹在一大堆不同的提供程序中。
字符串
当您在组件中调用
useRouter()
时,它会尝试从AppRouterContext
获取路由器示例,并在无法读取时抛出错误。型
在你的单元测试中组件是隔离呈现的。这意味着当你在测试中调用
render(<MyComponent />)
时,它对上面树中的提供者一无所知,这些提供者存在于真实的Next.js或普通的React应用程序中。因此,当在测试中的组件中调用
useRouter
,并使用来自next/navigation
的真实的useRouter
钩子,并且相应的上下文提供程序不可用/未传递正确的值时,它将尝试读取上下文,失败并抛出错误。通常,如React Testing Library文档所述,使用自定义
render
自动将测试中的组件 Package 在应用中存在的所有提供程序中是一种很好的方法。另一种选择是模拟(或部分模拟)从上下文读取的模块,如下所示。
型
在上面的示例中,来自
next/navigation
模块的真实的useRouter
钩子被替换为不从上下文读取的mock函数(因此,invariant expected app router to be mounted错误将不再抛出),并返回一个带有几个方法的假router
对象。(push
,replace
),它们也被设置为mock函数。如果它适合您的需求,您还可以模拟其他导出/方法(如
useSearchParams
和usePathname
)并更改它们的实现。这样的设置可以在测试设置文件中只完成一次,也可以在每个测试的基础上完成。