【问题标题】:Mock function of React Native component with EnzymeReact Native 组件与 Enzyme 的 Mock 功能
【发布时间】:2017-11-29 12:14:58
【问题描述】:

我正在尝试使用 Enzyme 和 Jest 进行单元测试,以模拟扩展 React.Component 的类的函数,我发现可以模拟继承的 setState 函数,但组件中没有其他函数。

例如,我的 App 组件有一个在 onChangeText 事件上调用 setStateTextInput 和一个在 onPress 事件上调用 submitFilterTouchableOpacity

export default class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = { filter: '' }
  }
  submitFilter = () => {
    if (this.state.filter.length <= 0) {
      Alert.alert('Filter is blank');
    }
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.filterContainer}>
          <TextInput
            style={styles.filterInput}
            placeholder='Filter...'
            value={this.state.filter}
            onChangeText={(text) => {this.setState({ filter: text })}}
          />
          <TouchableOpacity
            style={styles.filterButton}
            onPress={this.submitFilter}>
            <Text style={styles.filterButtonLabel}>Go</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

使用相同的模式模拟setStatesubmitFilter,以及调用每个函数的相同模式:

import Adapter from 'enzyme-adapter-react-16';
import Enzyme from 'enzyme';

Enzyme.configure({ adapter: new Adapter() });

describe('interaction', () => {
  let wrapper
  let mockFn
  beforeEach(() => {
    wrapper = Enzyme.shallow(<App />)
    mockFn  = jest.fn()
  })
  describe('editing the filter input', () => {
    beforeEach(() => {
      wrapper.instance().setState = mockFn
      wrapper.find('TextInput').first().props().onChangeText('waffles');
    })
    it('should update the filter state', () => {
      expect(mockFn).toHaveBeenCalledTimes(1)
    })
  })
  describe('clicking filter button', () => {
    beforeEach(() => {
      wrapper.instance().submitFilter = mockFn
      wrapper.find('TouchableOpacity').first().props().onPress()
    })
    it('should invoke the submitFilter callback', () => {
      expect(mockFn).toHaveBeenCalledTimes(1)
    })
  })
})

只有第一次通过,我不确定使用什么方法来模拟 submitFilter 函数来验证它是否被调用?

  interaction
    editing the filter input
      ✓ should update the filter state (3ms)
    clicking filter button
      ✕ should invoke the submitFilter callback (18ms)

  ● interaction › clicking filter button › should invoke the submitFilter callback

    expect(jest.fn()).toHaveBeenCalledTimes(1)

    Expected mock function to have been called one time, but it was called zero times.

      at Object.<anonymous> (App.test.js:47:16)
      at tryCallTwo (node_modules/promise/lib/core.js:45:5)
      at doResolve (node_modules/promise/lib/core.js:200:13)
      at new Promise (node_modules/promise/lib/core.js:66:3)
      at tryCallOne (node_modules/promise/lib/core.js:37:12)
      at node_modules/promise/lib/core.js:123:15

有什么想法吗?

【问题讨论】:

    标签: react-native jestjs enzyme


    【解决方案1】:

    升级到需要适配器的 Enzyme 3 后,我也遇到了这个问题。我修复它的方法是查看 StackOverflow 上的 this answer

    根据migration guide,你收到的 instance() 实际上是底层 react 元素的转换。所以你没有得到实际的模拟方法。要解决此问题,您需要监视原型。

    import Adapter from 'enzyme-adapter-react-16';
    import Enzyme from 'enzyme';
    
    Enzyme.configure({ adapter: new Adapter() });
    
    describe('interaction', () => {
      let wrapper
      let mockFn
      let spy
    
      describe('editing the filter input', () => {
        beforeEach(() => {
          spy = jest.spyOn(App.prototype, 'setState');
          wrapper = Enzyme.shallow(<App />);
          wrapper.find('TextInput').first().props().onChangeText('waffles');
        })
        afterEach(() => {
          spy.mockRestore();
        })
        it('should update the filter state', () => {
          expect(spy).toHaveBeenCalledTimes(1);
        })
      })
      describe('clicking filter button', () => {
        beforeEach(() => {
          spy = jest.spyOn(App.prototype, 'submitFilter');
          wrapper = Enzyme.shallow(<App />);
          wrapper.find('TouchableOpacity').first().props().onPress()
        })
        afterEach(() => {
          spy.mockRestore();
        })
        it('should invoke the submitFilter callback', () => {
          expect(spy).toHaveBeenCalledTimes(1)
        })
      })
    })
    

    【讨论】:

    • 嗨,感谢@worldlee78 的回答,我最终将相同的问题发布到了 github 中的 Enzyme 存储库,谁指出了你所做的 - spy on the prototype,再次感谢!
    • 不客气@erikse,如果解决方案适合您,您介意选择我的答案作为正确答案吗(所以其他人也可能找到它?)
    猜你喜欢
    • 2019-07-26
    • 2018-12-16
    • 2020-08-12
    • 1970-01-01
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 2021-01-30
    • 2020-05-30
    相关资源
    最近更新 更多