[英]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.