You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:55:01