【问题标题】:Mock Function or variable before shallow Jest浅玩笑之前的模拟函数或变量
【发布时间】:2020-11-17 13:40:36
【问题描述】:

我正在尝试写笑话来测试我的组件。但是一旦调用了浅层,我的测试就会失败,因为调用 fetchUsers 函数说无法读取 null 的属性获取(这似乎是 myRequestor 的初始值)。它无法在 MyComponent 内的 MyReqUtil 功能组件中初始化 myRequestor 的值。看起来它没有调用此函数或没有初始化值。我被困在这个问题上,因为我无法每次都编写测试只是在我使用 shallow 的第一行失败了。任何人都可以帮我解决这个问题。

MyComponent.jsx

let myRequestor = null;

 const MyReqUtil = () => {
 myRequestor = React.useContext(MyRequestorContext);
 return <ApplicationLoadingOverlay isOpen backgroundStyle="clear" />;
};


class MyComponent extends React.Component {

componentDidMount() {
  this.setState({ isLoading: true });
  this.fetchUsers();
}

fetchUsers() {
this.setState({ isLoading: true });
const { request } = myRequestor.get({
  url: '/users',
});

request
  .then(({ data }) => {
    if (Object.prototype.hasOwnProperty.call(data[0], 'error')) {
      this.setState({
        error: 'Failure', errorMsg: data[0].error, isOpen: true, isLoading: false, userItems: [{ name: 'DEFAULT', id: 'Default' }],
      });
      return;
    }

    const users = data.map(user => ({
      name: user.shortName,
      id: user.key,
    }));

    this.setState({
      userItems: users, isLoading: false,
    });
  })
  .catch((error) => {
    console.log(error);
    this.setState({ error: 'Failure', isOpen: true, isLoading: false });
  });
 }

 render() {

   if (this.state.tenantItems === null) {
      return <MyReqUtil />;
   }
}

MyComponent.test.jsx

describe('<MyComponent />', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<MyComponent />);
  });

     it('should check `componentDidMount()`', () => {
        const instance = wrapper.instance();
        jest.spyOn(instance, 'fetchUsers');
        instance.componentDidMount();
        expect(instance.fetchUsers).toHaveBeenCalledTimes(1);
     });

   it('Should Render Component Correctly', () => {
    const spy = jest.spyOn(OrionReqUtil, 'fetchUsers');
    const wrapper = shallow(<MyComponent />);
    const instance = wrapper.instance();
    spy.mockReturnValue(['mockItem1', 'mockItem2', 'mockItem3']);

   });
});

【问题讨论】:

  • 一个地方是MyReqUtil,另一个地方是OrionReqUtil。您在测试方面遇到问题是一种症状。当糟糕的设计决策使单元更难测试并需要重构时,实际上就是这种情况。
  • 很抱歉它实际上是 MyReqUtil。我已经编辑了代码

标签: reactjs react-native jestjs enzyme


【解决方案1】:

测试在beforeEach 和it 中执行了两次shallow(&lt;MyComponent /&gt;),这可能是一个错误。如果测试没有完全共享beforeEach 中的代码,则应将其移至测试。

disableLifecycleMethods 选项就是这种情况,它允许在准备好实例时手动调用componentDidMount:

wrapper = shallow(<MyComponent />, { disableLifecycleMethods: true });
jest.spyOn(wrapper.instance(), 'fetchUsers').mockReturnValue(...);
wrapper.instance().componentDidMount();

对于原型方法,另一种方法是在实例化之前在类原型上窥探或模拟它们:

jest.spyOn(MyComponent.prototype, 'fetchUsers').mockReturnValue(...);
wrapper = shallow(<MyComponent />);

这在这种情况下无济于事,因为MyComponent 依赖于MyReqUtil 子级才能正确渲染和安装,因此不适合使用shallow 进行测试。

这实际上是 XY 问题。由于是MyReqUtil 引起了问题,因此首先需要解决它。首先,它是一个不同的单位。这意味着它需要移动到另一个模块,因此可以使用jest.mock 进行模拟。

另外,MyReqUtil 与 MyComponent 建立了不健康的关系,它是一个为父级维护隐藏全局状态 (myRequestor) 的子级,并假定在挂载父级之前它不需要它。如果有多个上下文,这将停止按预期工作 - 即使在生产中没有发生这种情况,在测试中也可能需要这样做。

如果它唯一的作用是为MyComponent 提供依赖项,那么它应该是MyComponent 的父级,而不是相反:

const MyReqUtil = (props) => {
  const myRequestor = React.useContext(MyRequestorContext);
  const loaderElement = <ApplicationLoadingOverlay isOpen backgroundStyle="clear" />;

  return <MyComponent loader={loaderElement} requestor={myRequestor} {...props} />
};

这样它就可以作为一个单独的单元进行全面测试。并且MyComponent 可以进行全面测试,因为依赖项可以作为 props 使用,因此可以进行模拟。

【讨论】:

  • 我现在收到以下错误“无法监视 fetchUsers 属性,因为它不是函数;如果我使用浅层,则给出未定义。另外我无法更改主类,有没有办法编写同班考试??
  • 修复它,它应该是wrapper.instance(),而不是wrapper.instance。如果您无法重构组件,请尝试mount 或render。使用 shallow 时没有正确编写。
  • @k-wasilewski 如果在实例上使用它就可以。由于 fetchUsers 是 proto 方法,它也可以在 proto 上工作。出于几个原因,Proto 方法更受欢迎,只是因为它们更有效,并且提供了更多选项来监视或模拟测试中的方法。
  • 我之前没有使用过渲染,并且 mount 不适用于这个测试。你能帮我看看我如何用渲染而不是浅@Estus Flask 来写这个
  • 或者我只是硬编码 fetchUsers 函数的任何模拟返回值,这样它甚至不会调用 myrequestor.get??
猜你喜欢
  • 2023-02-03
  • 2020-07-21
  • 1970-01-01
  • 2019-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-07
相关资源
最近更新 更多