shichongrui / react-native-test-utils

A test utils library for testing react native components
24 stars 5 forks source link

Question about mocking components #14

Open bcarroll22 opened 5 years ago

bcarroll22 commented 5 years ago

👋 hi Matthew! My name is Brandon. I happened across this because I wrote a similar testing library for react-native. I love how simple the API is for this package, it seems very easy to use!

I noticed that you're pulling in the mockComponent definition from react-native and using it to mock a few extra components like the Touchables. I think I understand why you did it, but wanted to raise a concern that I have with it. I'm guessing you did it so that your CSS-like queries would work because mock creates an "string" version of the components that can be queried this way.

From my research in creating my library, I've concluded that the internal mocks are the way they are because their mocked components are the components that actually communicate across the batched bridge. Therefore those are the components that are most like the "DOM" in a library like react-testing-library. The way react native Touchables work internally are by rendering a View with gesture responders that handle the touch.

I'm not suggesting you change your implementation, just wanted to point it out because I think this wave of testing focus from libraries like yours and mine can serve to teach people more about the way React Native actually works. I just wanted to pose the question: rather than allowing queries on these sort of "wrapper components", would it be better to only allow queries on the actual "native" components?

I think the benefits to this are two-fold:

  1. You would avoid users having to mock every custom component they want to test.
  2. Not letting them test their components would feel inconsistent. For instance, the Touchables you mocked are essentially that. They're basically internal "custom" components that don't actually do native things themselves.

All of this to say, I love what you're doing and I can see the influence of the testing-library API. My fear is that we may miss an important opportunity to teach the community about the way RN works "behind the curtain". I think this increased understanding will only serve to sharpen the community.

Again, great job! Hope your users are enjoying it 💯

bcarroll22 commented 5 years ago

Actually quickly peeking through your open issues, this may be popping up already like it is in #7. Hope my explanation helps!

DaKaZ commented 5 years ago

@bcarroll22 thank you for this! I am intrigued and want to learn more. I am a big fan of actually going under the covers to figure this stuff out, but react-native is so large I often find myself just accepting ignorance and moving on... so if you can help me understand, that would be great.

rather than allowing queries on these sort of "wrapper components", would it be better to only allow queries on the actual "native" components

What specifically do you mean here? I think one of the biggest reasons that the Touchable* components are now mocked by default is that we were constantly adding jest.mock('TouchableOpacity', () => 'TouchableOpacity') so that we could actually simulate the onPress event and confirm functionality. In my 2+ years of RN application development, I haven't found another way of performing that simulation in my jest code. So specifically, if I have:

<TouchableOpacity
  testID='myButton'
  onPress={this.buttonHandler}
>
  <Text>Press Me</Text>
</TouchableOpacity>

How do I simulate an onPress for that object with the myButton test ID?

bcarroll22 commented 5 years ago

Hey sorry it took me so long to get back with you. Long story short, my only update is that I agree with you now. It's very hard to understand what React Native is doing internally to make things work and I don't actually think any testing library could be reasonably expected to teach it.

In NTL, the way we handle it is we bubble events to a handler that has the event and is a proper responder to that event. So in your example, this is what you would do:

import { fireEvent, render } from 'native-testing-library';

const { getByText } = render(
  <TouchableOpacity
    testID='myButton'
    onPress={this.buttonHandler}
  >
    <Text>Press Me</Text>
  </TouchableOpacity>
);

fireEvent.press(getByText(/press me/i));

which will fire this.buttonHandler and do whatever it does to update the rendered tree.

Side note, I just release a new version of native-testing-library to more closely align to dom-testing-library. One of the things I didn't have time to address was using CSS selector syntax to filter the results of a query. I would love to see how your CSS parser logic could help us implement that feature in NTL. The main goal is to consolidate the testing library and native testing library docs so you can learn once test anywhere. If you ever get bored or have some free time to tinker, I'd love to review a PR 😁