【问题标题】:Jest tests with i18next and storybook使用 i18next 和 storybook 进行 Jest 测试
【发布时间】:2018-07-28 13:50:47
【问题描述】:

我在我的组件仓库中使用 i18next。组件工作正常,但我在测试时遇到问题。我将 i18next 与 hoc 一起使用,当我只导出组件时,测试通过了,但是当我像 export default translate ('components') (List) 一样导出它时;测试失败。我尝试进行两个导出,使用 hoc 和不使用,但另一个使用了一些组件,如果没有 hoc,我无法导入它。我的架构看起来像:带有 I18nextProvider 的根组件,我用这个组件将每个组件包装在故事中,它是我主应用程序中的主要组件。看起来是这样的:

const Root = ({ component, i18 }) => (
  <I18nextProvider i18n={i18}>
    <div className={bindClasses('wrapper')}>
      {component}
    </div>
  </I18nextProvider>
);

示例组件:

import React, { Component } from 'react';
import { translate } from 'react-i18next';
import classNameBind from 'classnames/bind';
import styles from './List.css';
import Icon from '../Icon';
import uuid from '../../utils/methods/uuid';

class List extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: props.items,
    };
  }

  renderNoItems() {
    const { items, t } = this.props;

    return items.length === 0 ?
      t('noMatchingFoundLabel') : '';
  }

  renderItems() {
    const { onItemSelected } = this.props;
    const { items } = this.state;

    return items
      .map(element =>
        (
          <li key={uuid()}>
            <a role="button" tabIndex={0} onClick={() => onItemSelected(element)}>
              { element.icon ? <Icon {...element.icon} /> : '' }
              {element.name}
            </a>
          </li>
        ),
      );
  }

  render() {
    const { className } = this.props;
    const bindClasses = classNameBind.bind(styles);

    return (
      <nav
        ref={(scrollable) => { this.scrollable = scrollable; }}
        className={bindClasses('wrapper', className)}
        onScroll={this.handleScroll}
      >
        <ul>
          {this.renderItems()}
        </ul>
      </nav>
    );
  }
}

export default translate('components')(List);

每个组件都有 index.js 文件,只是默认导出。

故事中的用法:

<Root i18={i18}>
    <List />
</Root>

我不确定这里。我应该将 t 函数传递给 List 组件吗?没有它它可以工作,但也许我应该。 并测试:

import React from 'react';
import List from '../List';
import { shallow, mount } from 'enzyme';
import Icon from "../../Icon";
import TextField from "../../TextField";

describe("List component", () => {
  const defaultPropsWithIcons = {
    items: [
      { name: 'Item1', icon: { name: 'upload', color: 'default' }, options: { some: 'options1'} },
      { name: 'Item2', icon: { name: 'upload', color: 'default' }, options: { some: 'options2'} },
    ],
    t: jest.fn(),
  };

  test("should render List component with passed items with icons", () => {
    const wrapper = shallow(<List {...defaultPropsWithIcons} />);
    const actionList = wrapper.find('.wrapper');

    expect(list.find('ul').children().length).toBe(2);
    expect(list.find('ul').children().at(0).find('a').exists());
     expect(list.find('ul').children().at(0).find(Icon).exists()).toBe(true);
  });

正常导出组件可以正常工作,但翻译失败。 我试过用

jest.mock('react-i18next', () => ({
    receive the t function as a prop
    translate: () => List => props => <List t={() => ''} {...props} />,
  }));

或

const wrapper = shallow(<List {...defaultPropsWithIcons} t={key => key} />);

但这没有帮助,在第 1 次预期时仍然出现错误(预期为 2,收到 0) 任何人都知道我应该如何测试它?或者我在 i18next 使用中有什么错误?在我在另一个组件中使用 List 组件或仅在具有 i18next 的其他组件中使用 List 组件的每次测试中都遇到了同样的问题。

问候

【问题讨论】:

    标签: javascript reactjs jestjs i18next


    【解决方案1】:

    您不应该测试封装在 HOC 中的组件,因为单元测试应该测试代码的各个部分。

    在计算机编程中,单元测试是一种软件测试方法 哪些单独的源代码单元。 https://en.wikipedia.org/wiki/Unit_testing

    尽量避免在玩笑中使用 { mount },我怀疑你在使用它时遇到了麻烦,因为它会渲染子组件。

    如果您想要进行集成测试并渲染 ne=sted 组件,您可以像这样使用 mock:

    import { translate } from 'react-i18next';
    jest.mock('react-i18next', () => ({
        translate: () => List => props => <List t={() => ''} {...props} />,
      }));
    

    更新:

    如果您不喜欢将导入标记为未使用,您可以测试翻译组件:

    import { translate } from 'react-i18next';
    jest.mock('react-i18next', () => ({
        translate: jest.fn(() => List => props => <List t={() => ''} {...props} />),
      }));
    
    test('should test translation', () => {
        expect(translate).toHaveBeenCalled();
        expect(translate).toHaveBeenCalledWith('components');
      });
    

    希望对您有所帮助,祝您编码愉快!

    【讨论】:

    • 我之前用过这个模拟,但测试仍然失败......我不知道
    • 在你的代码中,我没有看到 import { translate } from 'react-i18next',你应该明确地这样做以模拟依赖
    • 我知道我知道,忘了说我添加了 import ;) 但仍然没有。而且我不知道为什么 import 还是被标记为未使用。
    • 只是因为它未使用 :) 像在更新中一样使用它,并尽量避免使用 jest.mount 这是您问题的根源...您也可以尝试将翻译作为上下文传递,不确定如果是这样的话。请提供错误描述
    • 随着您的更新,我期望(jest.fn())[.not].toHaveBeenCalled() jest.fn() 值必须是模拟函数或间谍。收到:函数:[函数翻译]
    猜你喜欢
    • 2021-07-02
    • 2019-01-29
    • 2020-05-05
    • 2023-02-21
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-01
    • 2018-12-11
    相关资源
    最近更新 更多