2

I'm trying to get going unit testing using Enzyme and Jest to mock functions of a class that extends React.Component, and I'm finding can mock the inherited setState function, but no other functions in the component.

For example, my App component has a TextInput that invokes setState on an onChangeText event, and a TouchableOpacity that invokes submitFilter on an onPress event.

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>
    );
  }
}

Using the same pattern for mocking setState and submitFilter, and the same pattern for invoking each function:

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)
    })
  })
})

Only the first passes, and I'm not sure what approach to use for mocking the submitFilter function to verify it gets invoked?

  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

Any ideas?

erikse
  • 21
  • 1
  • 3

1 Answers1

0

After I upgraded to Enzyme 3, which required an adapter, I also ran into this problem. The way I fixed it was by looking at this answer on StackOverflow.

According to the migration guide, the instance() you receive is actually a transformation of the underlying react elements. So you are not getting the actual method to mock. To fix this problem, you need to spy on the prototype.

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)
    })
  })
})
Lee Vaughn-Ogin
  • 429
  • 4
  • 9
  • Hi, thanks for this answer @worldlee78, I ended up posting the same question to the Enzyme repo in github, who pointed out as you did - to [spy on the prototype](https://github.com/airbnb/enzyme/issues/1432), thanks again! – erikse Dec 27 '17 at 02:33
  • You're welcome @erikse would you mind selecting my answer as the correct one if the solution works for you (so others might find it too?) – Lee Vaughn-Ogin Dec 29 '17 at 23:43