How to test a button click in React with TypeScrip

2019-07-03 03:30发布

问题:

I'm building a React Native app with TypeScript. I'm doing my component tests using Jest and Enzyme. I'm also using React Navigation

I'm struggling to write the unit test for clicking my button.

Here is the component's code (just the render function):

  render() {
    const { navigation } = this.props;
    return (
      <View style={styles.container}>
        <Button
          raised
          title={strings.painButtonTitle}
          buttonStyle={styles.painButton}
          titleStyle={styles.title}
          onPress={() => navigation.navigate("QuestionsScreen")}
        />
      </View>
    );
  }

Now here is the unit test.

  describe("interaction", () => {
    let wrapper: ShallowWrapper;
    let props: Props;
    beforeEach(() => {
      props = createTestProps({});
      wrapper = shallow(<HomeScreen {...props} />);
    });

    describe("clicking the Pain Button", () => {
      it("should navigate to the 'QuestionsScreen'", () => {
        wrapper.find("Button").simulate("click");

        expect(props.navigation.navigate).toHaveBeenCalledTimes(1);
      });
    });
  });

This fails and claims the navigation.navigate function - which is mocked using jest.fn() - is never called.

I assume it has something to do with providing an error function to the button. The problem is, I can't not do that, since I need to call with the argument "QuestionsScreen". So I can't do something like onPress={this.navigation.navigate) and then miraculously call "QuestionsScreen", can I?

How can I get this test to pass?

回答1:

The "Common Gotchas" section for simulate says that "even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it."

That behavior can be seen in the Enzyme source code here and here.

So the line wrapper.find("Button").simulate("click"); ends up trying to call the onClick prop of the Button which doesn't exist so it essentially does nothing.

This post from an Airbnb dev recommends invoking props directly and avoiding simulate.

Here is a modified test that invokes the onPress prop directly:

it("should navigate to the 'QuestionsScreen'", () => {
  wrapper.find(Button).props().onPress({} as any);

  expect(props.navigation.navigate).toHaveBeenCalledTimes(1);   // SUCCESS
});


回答2:

I had a similar question with you. I tried simulate("click") but failed because there's no onClick property of the component.

My way was to change the test case to wrapper.find("form").simulate("submit") because my navigation function was defined in the submit property of the form.