Jest结合TS复用Storybook故事时报id属性类型不兼容错误
问题现象
使用Jest + React Testing Library复用Storybook故事编写单元测试时,向渲染的故事传入故事中预定义的args参数,抛出Types of property 'id' are incompatible类型错误。
相关依赖版本
"@storybook/react": "^6.5.9", "@storybook/testing-react": "^1.2.4", "@testing-library/react": "^13.3.0", "jest": "^28.1.1", // 其余依赖省略
问题相关代码
测试文件代码
报错位置为render(<Checkbox {...Checkbox.args} />)行。
import * as React from 'react'; import { screen, render } from '@testing-library/react'; import { Default as Checkbox } from '../../stories/AccountCheckbox.stories'; const renderCheckbox = () => { render(<Checkbox {...Checkbox.args} />); // 类型报错抛出位置 } describe('<AccountCheckbox />', () => { test('should display account information', () => { renderCheckbox(); expect(screen.getByTestId('example-test-id')).toBeInTheDocument(); }); });
Story文件代码
import React from 'react'; import { ComponentMeta, ComponentStory } from '@storybook/react'; import AccountCheckbox from '../components/AccountCheckbox'; import { StoryAppWrapper } from '../../.storybook/wrapper/StoryAppWrapper'; export default { title: 'AccountCheckbox', component: AccountCheckbox, decorators: [(Story) => <StoryAppWrapper>{Story()}</StoryAppWrapper>] } as ComponentMeta<typeof AccountCheckbox>; const Template: ComponentStory<typeof AccountCheckbox> = (args) => ( <AccountCheckbox {...args} /> ); export const Default = Template.bind({}); Default.args = { id: 'example-id', testId: 'example-test-id', label: 'Account 1', selected: false, onChange: () => { }, };
组件类型定义
export type CheckboxProps = { id: string; testId: string; label: string; selected: boolean; onChange: any; };
组件实现代码
import * as React from 'react'; import { Checkbox } from 'custom-lib'; import { CheckboxProps } from '../../types/types'; const AccountCheckbox = ({ id, testId, label, selected, onChange, }: CheckboxProps): JSX.Element => { return ( <Checkbox id={id} data-testid={testId} label={label} state={selected ? 'true' : 'false'} onChange={onChange} /> ); }; export default AccountCheckbox;
报错根因
类型错误来自错误的Story渲染写法,核心问题有两点:
- 类型不匹配:直接从stories文件导入的
Default是Storybook内部封装的Story函数,它的Props类型除了业务组件的CheckboxProps,还包含Storybook注入的上下文属性(比如故事唯一标识id、渲染上下文参数等)。手动展开Checkbox.args传入时,TypeScript会将业务定义的id: string和Story上下文内置的id属性做类型校验,两者类型定义不兼容直接抛出错误。 - 逻辑缺失:手动渲染原始Story对象、手动传args的写法,会跳过
@storybook/testing-react的自动处理逻辑,包括全局/故事级装饰器(比如代码中配置的StoryAppWrapper包装器)、默认args合并、参数类型对齐等,就算绕过类型校验,运行时也大概率因为缺少上下文出现渲染异常。
修复方案
使用@storybook/testing-react官方提供的composeStories工具处理导入的Story对象,工具会自动完成类型对齐、装饰器包装、args绑定,不需要手动展开args。
修复后的测试文件代码如下:
import * as React from 'react'; import { screen, render } from '@testing-library/react'; // 引入官方Story处理工具 import { composeStories } from '@storybook/testing-react'; // 导入stories文件的所有导出 import * as CheckboxStories from '../../stories/AccountCheckbox.stories'; // 处理所有故事,自动完成类型转换、装饰器绑定、args注入 const { Default: Checkbox } = composeStories(CheckboxStories); const renderCheckbox = () => { // 直接渲染处理后的Story即可,默认自动带入Default.args配置 render(<Checkbox />); // 若需要覆盖默认args,直接作为props传入即可,类型会自动和CheckboxProps对齐 // 例:render(<Checkbox testId="overwritten-test-id" />) } describe('<AccountCheckbox />', () => { test('should display account information', () => { renderCheckbox(); expect(screen.getByTestId('example-test-id')).toBeInTheDocument(); }); });
内容的提问来源于stack exchange,提问作者Hyun Jae Lee
相关产品推荐
相关产品推荐

