我的React应用程序有一个Item
组件,应该通过点击react-router-dom
Link
组件中的按钮将用户重定向到App
组件。我跟随了很多教程,这些教程给出了如何在不传递道具的组件中实现Link
的清晰示例。我的直觉告诉我,我的Item
组件道具可能会导致Cannot destructure property 'basename' of 'React__namespace.useContext(...)' as it is null.]
错误,因为只有当代码中没有Link
时,才能在Jest上运行测试。
What I tried
- 将
RouteSwitch
代码移动到index.js - 将
components
中的脚本移动到src
- 仔细检查我是否正确地遵循了教程
- 查看这个reactjs - Uncaught TypeError StackOverFlow答案是否与我的问题有关
谁能指出我在这里错过了什么?
item.js
import { Link } from "react-router-dom";
const Item = ({info},{addCart}) => {
return(
<>
<Link to="/" ><button>X</button></Link>
<img src={info.pic} alt={info.title}></img>
<h1>{info.title}</h1>
<p>{info.description}</p>
<button onClick={()=>{addCart(info)}}>Add</button>
</>
)
};
export default Item;
routeswitch.js
import React from "react";
import { BrowserRouter,Routes,Route } from "react-router-dom";
import App from "./App"
const RouteSwitch = () =>{
return(
<BrowserRouter>
<Routes>
<Route path="/" element={<App/>}/>
</Routes>
</BrowserRouter>
);
}
export default RouteSwitch;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import RouteSwitch from "./routeswitch"
import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<RouteSwitch/>
</React.StrictMode>
);
item.test.js
import React from 'react';
import { render, cleanup,screen, getByAltText} from '@testing-library/react';
import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event';
import Item from './item';
afterEach(cleanup);
const details = {pic: `a pic`, title:`a title`, description:`a description`}
const addCartMock =jest.fn();
test(`Content renders`,()=>{
render(<Item info={details} addCart={addCartMock}/>);
const image = screen.getByAltText(`${details.title}`);
expect(image).toBeInTheDocument();
expect(image).toHaveAttribute(`src`,`${details.pic}`);
expect(screen.getByRole(`heading`, {name:details.title})).toBeInTheDocument();
expect(screen.getByText(`${details.description}`)).toBeInTheDocument();
expect(screen.getByRole(`button`, {name: `Add`})).toBeInTheDocument();
});
test(`Add button click event`,()=>{
render(<Item info={details} addCart={addCartMock(details)}/>);
userEvent.click(screen.getByRole(`button`), {name: `Add`});
expect(addCartMock).toHaveBeenCalledWith(details);
});
在单元测试中Link
的问题是它需要提供路由上下文给它。导入MemoryRouter
并将组件呈现到其中,这样Link
组件就可以使用路由上下文。
的例子:
import React from 'react';
import { render, cleanup, screen, getByAltText } from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import Item from './item';
afterEach(cleanup);
const details = {
pic: "a pic",
title: "a title",
description: "a description",
};
const addCartMock = jest.fn();
test("Content renders", () => {
render(
<MemoryRouter>
<Item info={details} addCart={addCartMock} />
</MemoryRouter>
);
const image = screen.getByAltText(`${details.title}`);
expect(image).toBeInTheDocument();
expect(image).toHaveAttribute("src",`${details.pic}`);
expect(screen.getByRole("heading", { name: details.title })).toBeInTheDocument();
expect(screen.getByText(`${details.description}`)).toBeInTheDocument();
expect(screen.getByRole("button", { name: `Add` })).toBeInTheDocument();
});
test("Add button click event", () => {
render(
<MemoryRouter>
<Item info={details} addCart={() => addCartMock(details)} />
</MemoryRouter>
);
userEvent.click(screen.getByRole("button"), { name: "Add" });
expect(addCartMock).toHaveBeenCalledWith(details);
});
在Item
组件中访问props对象也有问题。只有一个单独的props对象传递给React组件,所有的props都是由它析构的。
const Item = ({ info, addCart }) => {
return (
<>
<Link to="/"><button>X</button></Link>
<img src={info.pic} alt={info.title} />
<h1>{info.title}</h1>
<p>{info.description}</p>
<button onClick={()=>{addCart(info)}}>Add</button>
</>
);
};
不确定是否是造成挑战的原因,但在您的item.js
中,您错误地解构了道具。你可以直接解构它,也可以使用道具并使用点符号来获取它的子元素:
const Item = ({info, addCart}) => {
return(
<>
// rest of your code
</>
)
};
或
const Item = (props) => {
return(
<>
<Link to="/" ><button>X</button></Link>
<img src={props.info.pic} alt={props.info.title}></img>
<h1>{props.info.title}</h1>
<p>{props.info.description}</p>
<button onClick={()=>{props.addCart(info)}}>Add</button>
</>
)
};
或
const Item = (props) => {
const {info, addCart} = props
return(
<>
<Link to="/" ><button>X</button></Link>
<img src={info.pic} alt={info.title}></img>
<h1>{info.title}</h1>
<p>{info.description}</p>
<button onClick={()=>{addCart(info)}}>Add</button>
</>
)
};