【问题标题】:How to test async function with spyOn?如何使用 spyOn 测试异步功能?
【发布时间】:2019-11-01 10:30:45
【问题描述】:

我正在尝试在 react 本机应用程序中测试异步功能。

class myClass extends React.Component {

  ...

  closeModal = async () => {

    if (someCondition) {
      await myFunction1();
    } else {
      await myFunction2();
    }

    this.props.navigation.state.params.onGoBack();
    this.props.navigation.navigate('Main');
  };

  ...

}

这是我的测试:

const navigation = {
  navigate: jest.fn(),
  state: { params: { onGoBack: jest.fn() } },
};

const renderComponent = overrides => {
  props = {
    navigation,
    ...overrides,
  };

  return shallow(< myClass.wrappedComponent {...props} />);
};


describe('When the user presses the close icon', () => {
    it('should close the modal', () => {
      const wrapper = renderComponent();
      const instance = wrapper.instance();
      const spyCloseModal = jest.spyOn(instance, 'closeModal');
      instance().forceUpdate();
      component
        .find({ testID: 'close-icon' })
        .props()
        .onPress();
      expect(spyCloseModal).toHaveBeenCalled(); // this is passed
      expect(navigation.navigate).toHaveBeenCalled(); // this is not passed
    });
});

看起来它卡在等待调用上。如果我删除等待调用,那么它就会通过。有人在另一篇文章中提到在 spyOn 之后使用 .and.callThrough 但它给了我这个错误

无法读取未定义的属性“callThrough”

【问题讨论】:

    标签: javascript unit-testing react-native jestjs enzyme


    【解决方案1】:

    解决方案之一是让您的测试 async 并运行 await (anything) 以将您的测试分成几个微任务:

    it('should close the modal', async () => {
          const wrapper = renderComponent();
          component
            .find({ testID: 'close-icon' })
            .props()
            .onPress();
          await Promise.resolve();
          expect(navigation.state.params.onGoBack).toHaveBeenCalled(); 
          expect(navigation.navigate).toHaveBeenCalledWith("Main");
        });
    

    我相信您在实例方法上不需要.forceUpdate.spyOn。一旦导航正确发生,它被调用的内部方法并不重要

    关于微任务与宏任务的更多信息:https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f

    另一种方法是使用宏任务(setTimeout(...., 0)

    it('should close the modal', (done) => {
          const wrapper = renderComponent();
          component
            .find({ testID: 'close-icon' })
            .props()
            .onPress();
          setTimeout(() => {
            expect(navigation.state.params.onGoBack).toHaveBeenCalled(); 
            expect(navigation.navigate).toHaveBeenCalledWith("Main");
            done();
        });
    }
    

    【讨论】:

      【解决方案2】:

      是的,你在正确的轨道上......问题是 closeModal 是异步的。

      到执行返回测试时,await 尚未完成,因此尚未调用 this.props.navigation.navigate

      测试需要等待closeModal 完成,然后才能断言navigate 已被调用。

      closeModal 是一个async 函数,所以它会返回一个Promise...

      ...您可以使用间谍来检索它返回的Promise...

      ...那么您可以在测试中调用await 以确保closeModal 已完成,然后再断言navigate 已被调用。

      这是一个简化的工作示例,可帮助您入门:

      import * as React from 'react';
      import { shallow } from 'enzyme';
      
      class MyClass extends React.Component {
        closeModal = async () => {
          await Promise.resolve();
          this.props.navigation.navigate('Main');
        }
        render() { return <div onClick={() => this.closeModal()}></div> }
      }
      
      test('MyClass', async () => {  // <= async test function
        const props = { navigation: { navigate: jest.fn() }};
        const wrapper = shallow(<MyClass {...props} />);
        const instance = wrapper.instance();
        const spyCloseModal = jest.spyOn(instance, 'closeModal');
        wrapper.find('div').simulate('click');
        expect(spyCloseModal).toHaveBeenCalled();  // Success!
        const promise = spyCloseModal.mock.results[0].value;  // <= get the Promise returned by closeModal
        await promise;  // <= await the Promise
        expect(props.navigation.navigate).toHaveBeenCalled();  // Success!
      })
      

      注意使用mockFn.mock.results 来获取closeModal 返回的Promise

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-29
        • 1970-01-01
        • 2021-01-18
        • 1970-01-01
        • 1970-01-01
        • 2013-08-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多