Hinrich
Hinrich

Reputation: 14033

Testing TextInput Component in react-native

I have some problems with testing TextInput changes in react-native with jest and enzyme.

My component that handles user input basically looks like this (simplified):

class Search extends React.PureComponent {
  onSearchTextChange = input => {
    // do something awesome
  };
  render() {
    return (
      <View>
        <TextInput
          onChangeText={debounce(this.onSearchTextChange, 800)}
        />
      </View>
    );
  }
}

I want to test the text input behaviour. This is what the test looks like right now:

it('should render a text input and react to user input', done => {
  const mockOnSearchTextChange = jest.fn();
  Search.prototype.onSearchTextChange = mockOnSearchTextChange;

  const tree = shallow(<Search />);
  const textInput = tree.find('TextInput');
  expect(textInput).toHaveLength(1);
  textInput.simulate('changeText', { target: { value: 'test' } });
  expect(mockOnSearchTextChange).not.toHaveBeenCalled();
  setTimeout(() => {
    expect(mockOnSearchTextChange).toHaveBeenCalledWith('test');
    done();
  }, 1500);
});

When running this test, I get this error message

Expected mock function to have been called with: ["test"]

But it was not called.

So the mocked function is never called as expected. What am I missing?

Upvotes: 8

Views: 12036

Answers (3)

Prakash Bokati
Prakash Bokati

Reputation: 191

There is no need to add another library. Jest and Enzyme can perform the required testing. Below is the definition of SearchBar component which receives a function as a prop. The function is called with the text typed.

const SearchBar = ({onSearch}) => {
  return (
    <View>
      <TextInput
        onChangeText={text => onSearch(text)}
      />
    </View>
  );
};

The testing can be carried as follows

    const onSearch = jest.fn();
    const wrapper = shallow(<SearchBar onSearch={onSearch} />);
    wrapper.find('TextInput').simulate('changeText', 'test search text');
    expect(onSearch).toHaveBeenCalledWith('test search text');

Upvotes: 6

David Castillo
David Castillo

Reputation: 4465

I was able to do this using react-native-testing-library.

// ...
import { render, act, fireEvent } from 'react-native-testing-library'
// ...
it ('does stuff', () => {
  const mock = jest.fn()
  const component = render(<Search onSearchTextChange={mock}/>)
  fireEvent.changeText(component.findByType(TextInput), 'test')
  expect(mock).toHaveBeenCalledWith('test')
})

Upvotes: 5

Aman Sharma
Aman Sharma

Reputation: 63

You can add and testID in your TextInput Field and accessibilityLabel as one of the props, add both for ios and android compatibility, Here is the TextInput code.

   <TextInput
         placeholder='Email Address'
         testID='#email'
         accessibilityLabel='#email'
         blurOnSubmit={ false }
         onEndEditing={this.onSubmitLogin}
         >
         </TextInput>

In your test file do a shallow rendering, here

    describe('Your component', () => {
     it('Component: renders correctly', () => {
     const tree = renderer.create(<YourComponent/>).toJSON();
     expect(tree).toMatchSnapshot();
  });
  it('Has  TextInput', () => {
   const tree2 = renderer.create(<YourComponent/>).toJSON();
   expect(findElement(tree2, '#email')).toBeDefined();
 });

});

Then define the function findElement above describe. With console.warn you should see your props of TextInput and in that your testID

   const findElement = function (tree2, element) {
   let result = undefined
   console.warn(tree2.children)
   for(node in tree2.children){
   if(tree2.children[node].props.testID = element) {
   result = true
   }
  }
   return result
 }

It will test for all available TextInputs .... Note: Shallow rendering will only work if the TextInput is only one level deep(Inside one view only) if it is nested refer here - https://medium.com/@AidThompsin/heres-how-you-unit-test-textinput-with-react-native-63e1f7692b17

Upvotes: 0

Related Questions