NextJS13中如何为Cypress测试Mock useRouter
如何在Next.js 13的Cypress测试中正确Mock useRouter?
问题场景
我有如下Form组件:
const Form = ({ data }: { data: BuildingAPIResponseData }) => { const dispatch = useDispatch() const { push } = useRouter() // 测试报错源于此处 // 其他代码 }
对应的测试代码:
import { Provider } from "react-redux" import { store } from "@/redux/store" import Form from "." import MockRouter from "../../../../../../cypress/mocks/next-router" import { BuildingAPIResponseData } from "../../types" describe("Building Details IForm", () => { const zipCode = "63110" const city = "Rodgau" const street = "Liebigstraße" const houseNumber = "2" const API = `my_api` const apiKey = Cypress.env("api_key") const data: BuildingAPIResponseData = { buildingType: "Ein-/Mehrfamilienhaus", familyHome: false, constructionYearClass: "1949", fullStoreys: 2, hasPV: false, livingSpace: 137.0, roofType: "Flat Roof", hasLivingFunction: true, } it("should make successful request", () => { cy.request({ url: API, headers: { Accept: "application/json", "Ocp-Apim-Subscription-Key": apiKey, }, }).then((response) => { expect(response.body[0]).to.have.property("buildingType", data.buildingType) expect(response.body[0]).to.have.property("familyHome", data.familyHome ? "True" : "False") expect(response.body[0]).to.have.property("hasPV", data.hasPV) expect(response.body[0]).to.have.property("fullStoreys", data.fullStoreys) expect(response.body[0]).to.have.property("livingSpace", data.livingSpace) expect(response.body[0]).to.have.property("roofType", data.roofType) expect(response.body[0]).to.have.property("hasLivingFunction", data.hasLivingFunction) }) }) it("should fill the form with the default data", () => { cy.mount( <Provider store={store}> <Form data={data} /> </Provider> ) }) })
测试时出现报错,我尝试以下Mock方式但无效:
beforeEach(() => { const pathname = "http://localhost:3000/11127569/energy-check/building-details/summary" const push = cy.stub() cy.stub(NextRouter, "useRouter").returns({ pathname, push }) })
正确解决方案
1. 确认useRouter的导入来源
Next.js 13中,useRouter的导入路径分两种:
- Pages Router:
import { useRouter } from 'next/router' - App Router:
import { useRouter } from 'next/navigation'
必须保证模拟的路径和组件中的导入完全一致。
2. 使用Cypress模块Stubbing
直接模拟对应模块的useRouter导出,示例如下:
针对Pages Router(next/router)
beforeEach(() => { const mockRouter = { push: cy.stub().as('push'), // 命名stub方便后续断言 pathname: "http://localhost:3000/11127569/energy-check/building-details/summary", // 可添加其他需要的router属性,如query、back等 } cy.stub(require('next/router'), 'useRouter').returns(mockRouter) })
针对App Router(next/navigation)
beforeEach(() => { const mockRouter = { push: cy.stub().as('push'), pathname: "http://localhost:3000/11127569/energy-check/building-details/summary", // App Router的useRouter还包含如replace、refresh等方法,按需添加 } cy.stub(require('next/navigation'), 'useRouter').returns(mockRouter) })
3. 测试中的断言(可选)
如果需要验证push方法是否被调用,可以用之前命名的stub:
it("should navigate on submit", () => { // 触发表单提交操作 cy.get('button[type="submit"]').click() // 断言push方法被调用 cy.get('@push').should('have.been.calledOnceWith', '/target-path') })
4. 常见问题排查
- 之前的错误是因为未正确导入
NextRouter模块,直接用cy.stub(NextRouter, "useRouter")找不到对应的导出 - 确保
require的路径和组件中import的路径完全一致,大小写也不能错 - 如果使用TypeScript,可能需要添加类型断言:
(useRouter as jest.Mock),但Cypress中通常无需额外配置
内容的提问来源于stack exchange,提问作者Ala Eddine Menai
相关产品推荐
相关产品推荐

