Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing functions inside stateless React component with Enzyme

I have a stateless component:

export default function TripReportFooter(props) {
  const { tripReport, user, toggleFavorite, navigation } = props;


  handleShare = async slug => {
    try {
      const result = await Share.share({
        message: `Check out this Trip Report:\n/p/${slug}/`
      });

      if (result.action === Share.sharedAction) {
        if (result.activityType) {
        } else {
          // shared
        }
      } else if (result.action === Share.dismissedAction) {
      }
    } catch (error) {
      alert(error.message);
    }
  };

  handleFavorite = async id => {
    const token = await AsyncStorage.getItem("token");
    toggleFavorite(id, token);
  };

  return (
    ... // handleFavorite and handleShare called with TouchableOpacities.
  );
}

It has two functions inside, handleShare and handleFavorite. I want to test these functions are called, and also that handleFavorite calls the prop function toggle favorite.

I tried wrapper.instance().handleFavorite(), but since it is a stateless component, it returns null.

Next someone on Stack Overflow suggested using a spy like so:

    wrapper = shallow(<TripReportFooter {...props} handleFavorite={press} />);
    wrapper
      .find("TouchableOpacity")
      .at(0)
      .simulate("press");
    expect(press.called).to.equal(true);

but this returned

'TypeError: Cannot read property 'equal' of undefined'.

What's the proper way to call these functions?

like image 341
peter Avatar asked Mar 15 '19 19:03

peter


People also ask

How do you test the functional component of an Enzyme?

To properly test stateful functional components, it would be good to start with mocking the useState hook along with the setState function. The SearchReposInput component's purpose is to handle text entered by the user and pass it to the Redux action on button press.

Can stateless components have functions?

Stateless components are those components which don't have any state at all, which means you can't use this. setState inside these components. It is like a normal function with no render method. It has no lifecycle, so it is not possible to use lifecycle methods such as componentDidMount and other hooks.

Should I use Enzyme for React testing?

Other times, Enzyme is better. If you want mimic real-world user interactions, the React Testing Library is the way to go because you can do the same with fireEvent functions. Meanwhile, Enzyme is better suited to situations where you have to match the state of React or some other function with state.


2 Answers

You first need to think about what you want to test. Is it implementation details or the interaction with your component? The latter is a much better mindset and standpoint so what I would do is to test the component from the interaction point of view.

I would (for handleShare):

  1. Mock the Share object methods that are being called inside the share function;
  2. Select the button I want to click/touch
  3. Click/touch the button
  4. Assert that the methods were called.

Now for the handleFavorite:

  1. Mock AsyncStorage.getItem;
  2. Create a fake toggleFavorite function that I would pass as props;
  3. Select the button I want to click/touch
  4. Click/touch the button
  5. Assert my toggleFavorite function has been called

If you want to test these functions individually you would have to extract them to the outside of the component and test them individually. But I would not advise this as it is not clean and extra work.

Hope it helps!

like image 162
Pedro Filipe Avatar answered Sep 20 '22 15:09

Pedro Filipe


Functions within a functional component aren't defined on the prototype or the functional component instance, you cannot directly spy on them

The solution here is to test out the internal implementation of the individual functions

For instance for handleFavourite function you can mock AsynStorage and pass on a mock function for toggleFavourite and then asset it its called on TouchableOpacity onPress simulation

You can check how to mock AsyncStore in this post: How to test Async Storage with Jest?

const mocktToggleFavourite = jest.fn();
wrapper = shallow(<TripReportFooter {...props} toggleFavourite={mocktToggleFavourite} />);
    wrapper
      .find("TouchableOpacity")
      .at(0)
      .simulate("press");
    expect(mockToggleFavourite).toHaveBeenCalled();

Similarly you can test the individual functionalities within handleShare by first mocking Share.share and then checking against each condition.For instance you can add an spy on window.alert and see if that is called

const windowSpy = jest.spyOn(window, 'alert');
wrapper = shallow(<TripReportFooter {...props} toggleFavourite={mocktToggleFavourite} />);
//Simulate event that calls handleShare
// Mock Share to give required result
expect(windowSpy).toBeCalledWith(expectedValue);
like image 25
Shubham Khatri Avatar answered Sep 17 '22 15:09

Shubham Khatri