NextJS useRouter() 在带有 Storybook 插件的单元测试中返回 null

use*_*872 6 jestjs next.js storybook react-testing-library

在 NextJS 应用程序中使用 Jest 和 React 测试库将我的故事集成到单元测试中时,我遇到了问题。在 Storybook 中,我的 NextJS 组件渲染时没有问题,Storybook Addon Next Router按预期工作。但是,在我的 Jest 测试文件中,我的测试抛出错误,因为 useRouter() 返回 null。我相信我已经正确设置了所有插件。

我的设置:

问题:

我已经根据文档设置了所有文件。故事在 Storybook 中渲染得很好,并且 useRouter() 的工作得益于 Storybook Next 插件。但是,composeStories()@storybook/testing-react 中的函数似乎无法从第一个插件正确初始化 Next router Provider。我的单元测试失败并出现以下错误:

测试套件无法运行
TypeError: 无法读取 null 的属性“区域设置”

并指向我的组件内的以下行:

// Events.tsx
const { locale = 'en' } = useRouter();
Run Code Online (Sandbox Code Playgroud)

这是我的测试文件:

// Events.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';
import * as stories from './Events.stories'


const { WithSeasonsAndEvents } = composeStories(stories);

describe('Events Screen', ()=> {
  render(<WithSeasonsAndEvents />);
  it('renders input data', ()=> {
    // set up test
  });
});
Run Code Online (Sandbox Code Playgroud)

我的故事文件:

// Events.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import Events from './Events';

export default {
  title: 'Events/Events Screen',
  component: Events
} as ComponentMeta<typeof Events>;


const Template: ComponentStory<typeof Events> = (args) => <Events {...args} />;

export const WithSeasonsAndEvents = Template.bind({});
WithSeasonsAndEvents.args = {
  // many args here
};
Run Code Online (Sandbox Code Playgroud)

这个故事在 Storybook 中呈现得很好。useRouter() 在我的故事中按预期工作,我可以使用它的所有属性,包括pathnamelocale。然而,由于某种原因,当 React 测试库渲染组合故事时,useRouter() 函数返回 null。

我尝试过的:

// jest-setup.ts
import '@testing-library/jest-dom/extend-expect';
import { setGlobalConfig } from '@storybook/testing-react';

// Storybook's preview file location
import * as globalStorybookConfig from './.storybook/preview';

setGlobalConfig(globalStorybookConfig);
Run Code Online (Sandbox Code Playgroud)
  • 已验证 jest 读取我的设置文件并找到我的 Storybook 预览文件
  • 已验证 .storybook/preview.js 和 .storybook/main.js 均符合使用指南:

预览.js

// .storybook/preview.js
import '../styles/tailwind.css';
import { RouterContext } from "next/dist/shared/lib/router-context"; // next 12 https://storybook.js.org/docs/react/writing-stories/parameters

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  nextRouter: {
    Provider: RouterContext.Provider,
    locale: 'en',
  },
}
Run Code Online (Sandbox Code Playgroud)

main.js

// .storybook/main.js
module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
    "../components/**/*.stories.@(js|jsx|ts|tsx|mdx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "storybook-addon-next-router",
  ],
  "framework": "@storybook/react"
}
Run Code Online (Sandbox Code Playgroud)
  • 故事在 Storybook 中渲染得很好,所有 useRouter() 功能都可以工作,当我 console.log useRouter() 返回值时,我获得了全套 NextJS useRouter 对象属性:
// >> console.log(useRouter()); inside Storybook
{
    route: "/",
    pathname: "/",
    query: {},
    asPath: "/",
    events: {},
    isFallback: false,
    locale: 'en'
}
Run Code Online (Sandbox Code Playgroud)
  • 但是,在单元测试中,当记录我的组件内部的返回值时useRouter();,它返回null. 可以理解的是,因为它为空,所以{ locale }变量赋值在我的单元测试中引发了错误。
  • 当记录变量的值时useRouter,在我的 Storybook 预览和单元测试中,我得到以下函数:
// >> console.log(useRouter.toString())
function useRouter() {
    return _react.default.useContext(_routerContext.RouterContext);
}
Run Code Online (Sandbox Code Playgroud)

有谁知道可能出什么问题吗?我对 Storybook 相当陌生,但我尝试过查看 GitHuB 问题和在线问题,但未能解决。如果fn 应该负责解析我的 Storybook 参数和参数,不知道为什么useRouter()在 Jest 中返回 null 。composeStories()任何见解将不胜感激。

小智 3

我正在运行同样的问题。我通过导出WithNextRouter为装饰器解决了这个问题preview.js

// preview.js

import { WithNextRouter } from 'storybook-addon-next-router/dist/decorators';

export const decorators = [WithNextRouter];
Run Code Online (Sandbox Code Playgroud)