Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example usage of isolate-components to replace enzyme shallow for testing hooks #43284

Merged
merged 3 commits into from
Nov 2, 2021

Conversation

tim-dot-org
Copy link
Contributor

Background: Enzyme's shallow renderer doesn't trigger useEffect hooks in newer functional components, so you would normally need to use mount instead which imposes some performance penalties and potentially has side effects that don't need to affect this component under test.
isolate-components provides an alternative shallow renderer that does trigger useEffect. It has a slightly different but equivalent api. This PR shows an example of refactoring a test that previously used mount.

Links

isolate-components docs
isolate-components npm
Enzyme issue

Testing story

Test passes!

Follow-up work

Communicate this alternative to team, progressively refactor mount tests and use in future tests against useEffect.

@tim-dot-org tim-dot-org requested a review from a team October 29, 2021 21:57
});

it('Displays page buttons on each page', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was testing functionality of the Pagination component

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does isolate-component actually prevent you from testing the contents of the Pagination component? just checking my understanding that it is actually more like shallow than mount in that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is more like shallow, it couldn't find the PaginationButton components in the tree

@@ -166,8 +156,7 @@ describe('FormController', () => {
it('Does not submit when the last page has errors', () => {
setupErrored();

submitButton().simulate('submit');
form.update();
Copy link
Contributor Author

@tim-dot-org tim-dot-org Oct 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isolate-components seems to synchronously update the component as events happen, which makes it easy to get rendered data (no need to manually trigger an update or rerender)

@davidsbailey
Copy link
Member

@tim-dot-org the code changes look good to me. my only reservation is that this adds a new library for folks who write frontend code to learn. however, since it offers a material benefit over our existing use of mount, I think this is worth the tradeoff.

@tim-dot-org
Copy link
Contributor Author

@tim-dot-org the code changes look good to me. my only reservation is that this adds a new library for folks who write frontend code to learn. however, since it offers a material benefit over our existing use of mount, I think this is worth the tradeoff.

I have the same reservations, tho this one does completely replace enzyme and has a simpler api. It is significantly less widely used though, and might have issues later on

@davidsbailey
Copy link
Member

the main alternative approach I'm seeing is to use shallow rendering with https://www.npmjs.com/package/jest-react-hooks-shallow , which makes useEffect work as you would expect. any thoughts on which seems better @tim-dot-org ?

the react team suggests using their own deep rendering solution and explicitly stubbing out child components, which seems worse. having a library to enforce that shallow rendering is happening seems like a big benefit for us.

also curious if @madelynkasula has opinions.

@davidsbailey
Copy link
Member

I think what we really want here is to merge this but have the understanding that we are still evaluating this option, like the tech radar idea that @agebhardt has been advocating for. right now we don't really have a process for evaluating new libraries -- once they are in the repo they are fair game to be copied indefinitely.

@tim-dot-org
Copy link
Contributor Author

the main alternative approach I'm seeing is to use shallow rendering with https://www.npmjs.com/package/jest-react-hooks-shallow , which makes useEffect work as you would expect. any thoughts on which seems better @tim-dot-org ?

the react team suggests using their own deep rendering solution and explicitly stubbing out child components, which seems worse. having a library to enforce that shallow rendering is happening seems like a big benefit for us.

also curious if @madelynkasula has opinions.

I looked into that one too, but we're using mocha (instead of jest), potentially it just works with mocha, but i didn't wanna try and force that integration. It also seems to cause issues with existing mount tests, vs isolate-components which lets us use an incremental approach. Since this doesn't have any wide reaching effects, i'd agree on merging and evaluating forward.

@tim-dot-org
Copy link
Contributor Author

One other thing to note! enzyme is planning on fixing shallow to work with useEffect and I believe they're planning on using isolate-components under the hood to implement that. It is unclear on the timeline for that to be done though

@davidsbailey
Copy link
Member

good point about jest vs mocha. very interesting that enzyme is planning to use isolate-components! from that and reading the github issues, it does kind of seem that isolate-components may have some momentum behind it. I'm game to head in that direction!

@maddiedierker
Copy link
Contributor

i feel pretty strongly that we need to move away from enzyme in the long term, likely toward react testing library (which isn't actually built/maintained by the react team), which has become the industry standard. airBnb transferred ownership of enzyme in early 2020, and the organization they transferred to is just 1 person. reading through the thread for this specific issue, it was opened in april 2019 and doesn't look like the maintainer will be able to address this anytime soon (which is understandable -- 1 person is holding this entire project on their back!).

i worry that rather than enzyme getting updates, we will need to continue looking to outside libraries to get the testing functionality we need as react progresses. this is a useful article that i think encapsulates why folks have transitioned away from enzyme toward react testing library; there are good reasons to transition even outside of the concern around long-term maintenance.

so, i don't think enzyme is the right direction in the long term, but that is obviously a big decision and a large amount of work. as tim mentioned, just getting react testing library set up may be a considerable task. (i don't actually have a good estimate, but i'm sure there are migration guides that could help us.) i plan to use react testing library in the component library; that's a new project, so the setup will be much easier, but should still give some insight into what it could look like in our main repo.

this doesn't really answer your question, i don't think 😄 i'm not opposed to using this library, but i think we need to make a plan for the future of our frontend unit testing. maybe this is a topic for the architecture working group? @aaronwaggener and i have had some discussion about enzyme and its future already, so i'll chat with him about it.

@tim-dot-org
Copy link
Contributor Author

Thank you for those insights! That seems very convincing that we should move away from enzyme and I'd be on board with that. I need to read up a bit more on RTL to see how those tests are structured, because I haven't actually used it yet (it didn't seem to have any way to shallow render, but maybe that isn't as necessary as we currently think it is?).

@davidsbailey
Copy link
Member

Thank you Maddie, that's great info! +1 for the goal of moving away from enzyme.

Thank you for those insights! That seems very convincing that we should move away from enzyme and I'd be on board with that. I need to read up a bit more on RTL to see how those tests are structured, because I haven't actually used it yet (it didn't seem to have any way to shallow render, but maybe that isn't as necessary as we currently think it is?).

@tim-dot-org the React docs at https://reactjs.org/docs/testing.html#tools argue that deep rendering + mocking is one way to do shallow rendering tests:

Although it doesn’t provide a way to “shallowly” render a component without its children, a test runner like Jest lets you do this by mocking.

I would much rather have a shallow rendering library, because then I don't have to remember to stub every child component in order to test my component in isolation. but if we had to use RTL for all our tests today then that's what we could convert our shallow rendering tests to do (which would be a whole lot of work). I'm not 100% sure this solution could be adapted from jest to mocha, so that could be worth investigating soon.

@tim-dot-org
Copy link
Contributor Author

If we were doing the work to switch off enzyme entirely to RTL, it might be worth looking into upgrading from mocha to jest at the same time.
I also agree that mocking everything would be a pain. RTL's opinion is definitely to not even mock if you can help it. Maybe the best approach would be to fully render most components, and use something like isolate-components to shallow render larger components when necessary.
see this article from the author of RTL: https://kentcdodds.com/blog/why-i-never-use-shallow-rendering

@davidsbailey
Copy link
Member

From Maddie's blog post link, I am also intrigued by this statement:

I still vividly remember installing and configuring in my project Mocha, Chai, Sinon, and JSDOM, to get pretty much the same tooling that today a single library – Jest – provides out of the box, while adding numerous features on top of that.

@madelynkasula perhaps the component library repo would be a good time to try out Jest, too!

@maddiedierker
Copy link
Contributor

yes, i'm planning to use jest as well! i've used it in the past and i definitely agree with the quote you pulled from that blog post 😄

@davidsbailey
Copy link
Member

OK, so after today's eng meeting, it seems like React Testing Library would be a great candidate for a tech proposal! Until then, we should clarify what the current best practices are.

I would propose the following best practices for our main repo for now, at least for when upgrading to functional components:

  1. it is ok to keep using enzyme shallow wrappers for unit tests when possible, but you can switch to isolate-components if you want to
  2. if you want to write / refactor a unit test with shallow rendering for a component which uses useEffect, use either
    a. isolate-components or
    b. enzyme mount combined with stubbing out child component dependencies
  3. when you need to write an "integration test", use enzyme mount

What do you think @madelynkasula and @tim-dot-org ? For now I mostly want to make as easy a path as possible for folks who want to upgrade existing components to use hooks.

@maddiedierker
Copy link
Contributor

i like that proposal! we can use the component library as a testing ground for RTL/jest, and introducing isolate-components will allow us to better test hooks functionality right now, while we decide how we're moving forward in our main repo, which will take a long time to fully transition

@tim-dot-org
Copy link
Contributor Author

sounds great! I will merge this PR then

@tim-dot-org tim-dot-org merged commit a76b43e into staging Nov 2, 2021
@tim-dot-org tim-dot-org deleted the try-isolate-component branch November 2, 2021 19:17
@davidsbailey
Copy link
Member

great! I will make a note to maybe try out the mount plus stubbing child components, then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants