Happy to see you over here at dev.to! :) To be honest I'm still very unexperienced with testing. Should I do it with a test project first to get into it?
(Also a cool feature for dev.to would be to mark specific posts as discussions, I'd like to use dev.to as my blog on my website).
Full-time web dev; JS lover since 2002; CSS fanatic. #CSSIsAwesome I try to stay up with new web platform features. Web feature you don't understand? Tell me! I'll write an article! He/him
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
We have some long overdue design improvements on the editor to help people select the right tags, so it's great to see this meta discussion help folks out.
Kent, between this and your other couple posts I think you're making really great use of the format of the site and look forward to more of this community-enabling content; using your de facto leadership role in JS/React to spark a lot of great discussions that have a bit more lasting value than a Twitter thread.
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
Full-time web dev; JS lover since 2002; CSS fanatic. #CSSIsAwesome I try to stay up with new web platform features. Web feature you don't understand? Tell me! I'll write an article! He/him
@bdbch if you're just trying to page through your own articles, you can probably just pull in the whole dang list and do whatever sorting and paging you want on the client. You'd have to write a ton of articles for that to be a problem. Different story if you're pulling more that just your own, but in that case you'll probably have a more specific query anyway
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Should I do it with a test project first to get into it?
You could do that if you like. I think you'll find that it's pretty easy to get up and going though. You can even try it in the very same file that you're using enzyme in as well! So migrating is a pretty simple process.
Overall I think it's great and solves many of the issues with enzyme.
My favorite parts are:
It prevents you from accessing the instance. This is the most common testing mistake I see in the code bases I work on-- calling .instance() and then directly invoking an internal method for testing.
It prevents you from querying by the component name, e.g. expect(wrapper.find(MyComponent)).toEqual(...). This is especially an issue when writing integration tests with enzyme, as the component structure is really just an implementation detail at that point. I think static rendering (render()) might be a better option, but most people just seem to use mount. In any case, that leads to my next point:
Much smaller, simpler API. Enzyme provides so much functionality that it can be really hard to figure out the right approach for a given situation. Even worse is that much of the functionality can lead to really brittle tests. There's just a lot less decision making with RTL.
The fact that RTL does not provide an instance() API certainly does discourage accessing the instance though, and I agree with that. I also agree with all your parts.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
I'll start. I love it! It enables me to write tests that resemble the way my software is used which gives me more confidence and makes tests easier to write (and read). I only need to change my tests when I actually change my app's behavior, and my tests actually help me catch bugs. Also, the fact that I don't have to change my tests as I refactor to hooks (and it handles the act() calls for me) is fantastic.
We picked up React Testing Library when we made the jump to React Hooks and were looking for a testing platform that supported it (enzyme did not). In the process we found that with RTL we could write better integration style tests with fewer test files and better code coverage.
Personally, I love the ideology behind RTL. Forcing your tests to interact with the DOM the way your users will makes the tests far more robust and descriptive and promotes much more expressive UI design.
As a side note, I love the updates that I have been seeing to RTL and the surrounding libraries. Great job!
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
I think a lot of people jumped on with the hooks thing. Enzyme now supports hooks (mostly anyway), but I think people are discovering that react testing library is a better way to test components anyway.
Inertia is hard to beat, so it will be a very long time before RTL has more downloads than enzyme, but I do believe it will happen eventually.
I've been using RTL personally and love it so far. The ideology behind it makes so much sense.
However, I have to use Enzyme at work and it makes following the idea behind RTL so much harder. Finding elements with Enzyme returns a bunch of implementation details that don't usually don't really help.
I had tried to convince the frontend architect at my company to switch over to RTL for testing. He had tried it but didn't like it for a couple of reasons:
There is no shallow rendering (I believe he loves unit testing components). He believes that each component and test should be isolated from one another, so that tests would not bleed into one another, making tests less brittle than rendering the entire tree.
Rendering the entire tree with RTL makes it really ez to have huge, unmaintainable tests. I told him it is possible to unit test with RTL, but he said the fact that the default is full DOM rendering just makes it to ez to not unit test.
I had argued that shallow rendering does not give the confidence that the code works, but he said there needs to be a balance for maintainability. I proposed adding RTL to the codebase to allow everyone to try it out but he argued that when a developer encounters a test in RTL, the developer would have to learn a whole different set of testing API to make changes to the test.
I honestly still think RTL makes the better trade-off though because I followed its ideology in Enzyme and it has actually caught bugs for me.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
I should write about this. People who complain that tests break a lot when testing this way don't understand that they're breaking for the right reasons whereas testing implemention details makes your tests break for the wrong reasons. Ugh.
:/ Ever since I learned about the RTL ideology, using Enzyme has just been pretty miserable for me. It's so ez to test an implementation detail, find elements that also return the Component element rather than just the DOM element.
At work, I mostly see shallow rendering, expecting this shallow wrapper to match snapshot, expecting wrapper.state('whatever') to equal something... Don't really wanna start any crap so I try not to say anything.
Also, I can't seem to find another reliable way in Enzyme to wait for an async componentDidMount besides returning the promise inside componentDidMount and awaiting. I don't really like this approach though... because it relies on implementation detail.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Have you tried using Jest mocks to replicate shallow rendering? If not, maybe try suggesting this approach to your architect. See FAQ for more info.
Using Jest mocks is actually a more powerful approach anyways, since it allows you to explicitly choose which components to mock. With shallow, all components are mocked, even if they're defined in the same file.
Yep. His concern was that it's too ez to not mock and then developers would put a bunch of tests into the component higher up in the tree, making it harder to maintain, whereas when shallow rendering just mocks as much as possible, encouraging to test whatever the current component does.
So far so good. I had one case however where I was unable to figure out one test case. I have a button, that button has text. Upon clicking the button, the text gets replaced by a spinner. Thing is, the width of the button should stay the way it was when text was inside and not squeeze to be spinner sized. I did a simple render, put the width of the component in a variable and then fired a click event. I then tried getting the width and seeing if it matched. It was 0 all the time. I’m a bit new to testing but assumed JSDom or JestDom is being used when I render() the component and it would simulate properly. I even tried wrapping the button in a container div with preset dimensions. No luck still. For other unit tests it was a breeze. It really made me rethink how I test my Frontend.
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
I think JSDOM only provides DOM APIs. It isn't a full-blown rendering engine which is required to determine element's visual and structural properties. You'd have to use some visual testing tool to validate the behavior you described.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
With JSDOM, you can't measure layout, but you can verify that the style/class name was applied and trust that in the browser's ability to lay it out properly based on that.
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
I love it! I constantly trying to push the library and thinking paradigm on everyone.
At the very least at my work, since they are still going to use Enzyme, I constantly am encouraging people to learn and research the paradigm (philosophy) that you have written about and the entire react-testing-library is built around... Ever since then, I have become the "go-to guy" for unit testing questions, I also have now done a few internal pieces of training. The tests have significantly improved, the coverage has increased and people have a better idea of WHY they are creating the tests just from shifting that mindset alone!
Really love RTL. We’re almost exclusively enzyme for frontend tests at work, but I’ve started introducing RTL in a few projects and everyone is really digging the design philosophy of it. Excited to keep using it going forward 👍
I love it and so does my team. I was an early adopter and introduced it to the team as an alternative approach to ReactTestUtils/snapshots. Testing components and workflows from the user's POV gives us enormous confidence and the ability to find elements using a11y tags kills two birds with one stone. We use it in conjunction with jest-dom and are over the moon with how reliable the products are.
The only real testing pain we've encountered has been lack of async act support but React 16.9 will fix this and we found plenty of useful workarounds thanks to conversations on the @testing-library/react GitHub page.
So thank you Kent and all the project contributors. Long live @testing-library!
I wrote tests for almost two years with jasmine and karma for Angular project.
Now I'm working with react, react testing library and wallabyjs and tests is not pain anymore. For me it was huge improvements in my workflow and I feel much more productive.
It's the shit. My coworkers and I love it. I think enzyme, frankly, is weird as fuck (wtf is shallow rendering and what does .dive do? I don't get it).
Everything about react testing library so far has been really awesome. Easy to set up, easy to use, encourages testing UI the right way, and is just generally dope af. Much respect.
I strongly agree with the guiding principles of RTL and have been using it for all new development that I can (specifically with the new Marko RTL wrapper). It has allowed us to catch severally A11Y issues already which is amazing since we already tried to avoid testing implementation details but making it easier to do goes a long way.
Having said that my current thinking is that for components there are really two people that need to be tested for. One is for the end user consuming your app, which is handled perfectly by RTL. The other of course is the consumer of the components.
In most frameworks the component consumer is interacting via props and events which are the public api for a component. To the end user it is an implementation detail, but it isn’t to the consumer. Having the ability to render and rerender components exists and is perfect for testing the prop API for components and there is usually some mechanism for testing events from the component such as spy’s. I definitely agree that accessing the component instance is not something the consumer would do and is an implementation detail, but testing props and events is and should be recommended to be tested.
All that is to say, I think RTL’s framework wrappers mostly handle that well, but I don’t feel like that clarification is made on the site. Primarily the wording appears to be about testing for the end user, which is great, but also the public api for components needs to be tested for the consumers of the component, which is often props and events emitted.
Unrelated, I have also been trying to reconcile the reasons why shallow rendering is so popular given the flaws that you mention in your article. I feel that your article highlights important points but might be a tad too absolute in its recommendation to avoid shallow rendering.
I think that using shallow rendering (especially with snapshots) does next to nothing as you say in terms of actually testing for the end user since 99% of the snapshot is testing implementation details. However I do think snapshots provide benefit to developers. They tell you when relevant sections of code has changed so that you can quickly and easily audit it, similar to comparing changes with git, except isolated to what an individual components renders.
However you could of course snapshot the entire rendered dom which would in fact give you more information about what was rendered, however I think at that point the level of noise is too much (similar to giant PR’s that no one wants to review) and so I think people opt for shallow rendering.
In Marko we are experimenting with a new technique which aims to snapshot the entire render tree while including minimal details about your child components. We are doing a full render of components (which means our equivalent of render props actually run which is nice!) and then are going to add the ability to clone the rendered DOM and strip out all nodes not owned by the component which you are testing. Meaning if you render a child component and pass children to it, only the children you passed would be in the snapshot, nothing rendered by the child.
And to end my list of ramblings, I feel that RTL (especially often being limited to JSDOM) is missing some tools to test as a user would, ie simulated scrolling, resizing, perhaps simpler tools for mouse and touch interactions as well? Perhaps good candidates for @testing-library/user-events.
Curious what your thoughts are on all this and thanks for your great work on this suite of tools!
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
for components there are really two people that need to be tested for.
Agreed! And I have a blog post on that here: Avoid the Test User. If you have ideas on how to improve the docs, please open issues/pull requests.
I'd like to add that many of the limitations are not React Testing Library's but actually JSDOM. If you want, you can use React Testing Library in the browser (with karma for example) and you avoid some of the issues you mentioned (though you get a few new ones).
And yes, user-event is the place for stuff like that 👍
I like the focus on testing via the DOM. I found that my tests for form fields interactions made my React more accessible, since I decided to use Labels and aria-labelledby tags properly. I also found that using title instead of data-testid led to more understandable buttons for the end user as well. Overall, great work. If I had even one suggestion, it would be to make the examples repo more interactive, perhaps with a demo or "playground" page where one can see the library in action.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
What resources would you recommend for learning frontend unit testing? I've written unit tests in the past but now I'm planning on learning it properly. TIA. 🙂
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
RTL is amazing. The API feels so intuitive and easy to use, and it's not overly complex. Writing tests is a breeze now compared to other tools I've used...it's enabled me to go full on TDD and get crazy good test coverage across all the components using it. Great work Kent :)
I'm sure RTL has been a lot of work and I'm not trying to call you out. It seems you're really passionate about this and legitimately open to feedback.
I think it doesn't solve the biggest problems with testing. Testing should be easy and perhaps even fun. I've yet to ever once try to install RTL on a site and have it just work. Instead I get stuck in configuration land. It does little to improve mocking, which is one of the more frustrating parts of testing, especially for a beginner. Once going is it better than Enzyme? Sure, I guess. But only because it's opinionated and so you do less. I'd say RTL is acceptable but doesn't improve the testing landscape in a meaningful way overall. For those familiar with testing it's a nice little api that's moderately helpful.
I suppose this isn't the goal of RTL. The goal is to test the things that need testing, not implementation details. I think that's a worthy goal, it's just never going to be improved until testing is easy to do. RTL doesn't really move the needle on ease of use. As such it's a needless improvement in my view. You could have done the entire RTL task with a VSCode package that highlights or underlines when bad enzyme practices are being used.
The rtl is really nice when it works. But it seems have some serious problem when Material UI. I literarily spent more than 2 days still trying to figure out why the userEvent.type won't work on a properly selected input dom via testid. Its very frustrating
Software Engineer at Uphold · Previously Software Engineer at MOXY.studio · Java/.NET Back-end and C/C++ Embedded Software Engineer at Fraunhofer Portugal AICOS
Location
Porto, Portugal
Education
M.Sc. in Electrotechnical and Computers Engineering
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Yes, though they're not simultated events. They're actual events like the ones the browser will fire when the user interacts with your component. Check it out here: testing-library.com/docs/dom-testi...
I get random "should call act" errors depending on which device I run the test. If fireEvent.click needs to be wrapped in act() to be dependable, it should use it itself. Instead I have to wrap it myself every time. My test code is ugly and long-winded.
It promises interaction like the user does, but I can't get "the sort icon beside the column header". It has a function "within" but its use seems to be more for typecasting from HTMLElement to whatever internal type it uses rather than looking in the HTMLElement's children.
I have to step outside the framework to use .closest(..) to find elements "like a user would".
I have to add role, name, and other nonfunctional attributes to several places to my code because I cannot find by icon... because I'm not allowed to find by the class which puts the icon there. (FontAwesome, etc.)
I end up with tests that navigate by a mishmash of screenreader landmarks and visual landmarks, so I have no guarantee that either method of navigation works for either type of user.
I can't string the element, findBy, click calls in a fluent interface. The closest I get is with findBy(...) because I can string on .then(fireEvent.click). But this randomly throws the "act(...)" error depending on device, even when teh whole line has await. So instead I'm forced to write tests that read like a dick-and-jane English primer instead of a more natural style.
I can't get "the only select element on the popup" like a user would because I can't find by HTML tagname.
GetByText gets text from closed dropdowns and non-visible popups, making it largely useless. (Bootstrap3)
Getting an easily visible element by a combination of factors isn't possible. All my column headers have "click to filter" title, all my column headers' texts appear again in a dropdown or two, i actually want the sort icon beside the text in the header but there's one per column.... I can only get/find/query by a single criteria. I'm reduced to array indexes.
I like the idea behind this framework, but it's implementation leaves a lot to be desired.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Anything that allows you to reference a component instance (like .instance().state(), .setState(), .props(), .setProps())
Anything that allows you to query for a component by the component's displayName (like .find())
Fundamentally, it's about testing implementation details, which enzyme enables and encourages. This leads to very poor tests which have many false positives and false negatives. Learn more from my blog post Testing Implementation Details.
I created an account here just to say: Enzyme…? Better to have called it "pile of shit stacked" 🤮, as you said: "looks like a minefield". Nothing works ... it's, awful, a waste of time using this fucking shit library. I have had a horrible experience using it. The authors would be better off dead.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
My name is Kent C. Dodds and I'm a Remix Co-Founder, JavaScript engineer, and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.
Happy to see you over here at dev.to! :) To be honest I'm still very unexperienced with testing. Should I do it with a test project first to get into it?
(Also a cool feature for dev.to would be to mark specific posts as discussions, I'd like to use dev.to as my blog on my website).
The #discuss tag is just for that!
#discuss
I agree add #discuss :)
Done! Thanks folks!
We have some long overdue design improvements on the editor to help people select the right tags, so it's great to see this meta discussion help folks out.
Kent, between this and your other couple posts I think you're making really great use of the format of the site and look forward to more of this community-enabling content; using your de facto leadership role in JS/React to spark a lot of great discussions that have a bit more lasting value than a Twitter thread.
👏👏👏
Ben...what do you think about react testing library lmao
Yep, see here:
dev.to/bdbch/comment/d8mn
I believe the #discuss tag is normally used for that. 😉
Can you exclude this tag when you want to fetch it via the articles endpoint?
I'd like to integrate dev.to into my website instead of medium but wouldn't like to see my discussion posts in there. :)
Sure could filter out on my side, but that would break the pagination.
Not sure. I haven't used the API yet. Maybe the docs can provide you a hint. 👌
@bdbch if you're just trying to page through your own articles, you can probably just pull in the whole dang list and do whatever sorting and paging you want on the client. You'd have to write a ton of articles for that to be a problem. Different story if you're pulling more that just your own, but in that case you'll probably have a more specific query anyway
Thats true, guess thats the best way to go from here on! :)
You could do that if you like. I think you'll find that it's pretty easy to get up and going though. You can even try it in the very same file that you're using enzyme in as well! So migrating is a pretty simple process.
Overall I think it's great and solves many of the issues with enzyme.
My favorite parts are:
It prevents you from accessing the instance. This is the most common testing mistake I see in the code bases I work on-- calling
.instance()
and then directly invoking an internal method for testing.It prevents you from querying by the component name, e.g.
expect(wrapper.find(MyComponent)).toEqual(...)
. This is especially an issue when writing integration tests with enzyme, as the component structure is really just an implementation detail at that point. I think static rendering (render()
) might be a better option, but most people just seem to usemount
. In any case, that leads to my next point:Much smaller, simpler API. Enzyme provides so much functionality that it can be really hard to figure out the right approach for a given situation. Even worse is that much of the functionality can lead to really brittle tests. There's just a lot less decision making with RTL.
RTL doesn't prevent you from accessing the instance since it's still possible to get the instance by
The fact that RTL does not provide an
instance()
API certainly does discourage accessing the instance though, and I agree with that. I also agree with all your parts.True, "prevent" was probably the wrong word to use here!
I use the word "enable"
I'll start. I love it! It enables me to write tests that resemble the way my software is used which gives me more confidence and makes tests easier to write (and read). I only need to change my tests when I actually change my app's behavior, and my tests actually help me catch bugs. Also, the fact that I don't have to change my tests as I refactor to hooks (and it handles the
act()
calls for me) is fantastic.Super awesome to see you posting on dev.to!!!! :)
We picked up React Testing Library when we made the jump to React Hooks and were looking for a testing platform that supported it (enzyme did not). In the process we found that with RTL we could write better integration style tests with fewer test files and better code coverage.
Personally, I love the ideology behind RTL. Forcing your tests to interact with the DOM the way your users will makes the tests far more robust and descriptive and promotes much more expressive UI design.
As a side note, I love the updates that I have been seeing to RTL and the surrounding libraries. Great job!
That's great to hear!
I think a lot of people jumped on with the hooks thing. Enzyme now supports hooks (mostly anyway), but I think people are discovering that react testing library is a better way to test components anyway.
Inertia is hard to beat, so it will be a very long time before RTL has more downloads than enzyme, but I do believe it will happen eventually.
I've been using RTL personally and love it so far. The ideology behind it makes so much sense.
However, I have to use Enzyme at work and it makes following the idea behind RTL so much harder. Finding elements with Enzyme returns a bunch of implementation details that don't usually don't really help.
I had tried to convince the frontend architect at my company to switch over to RTL for testing. He had tried it but didn't like it for a couple of reasons:
I had argued that shallow rendering does not give the confidence that the code works, but he said there needs to be a balance for maintainability. I proposed adding RTL to the codebase to allow everyone to try it out but he argued that when a developer encounters a test in RTL, the developer would have to learn a whole different set of testing API to make changes to the test.
I honestly still think RTL makes the better trade-off though because I followed its ideology in Enzyme and it has actually caught bugs for me.
I should write about this. People who complain that tests break a lot when testing this way don't understand that they're breaking for the right reasons whereas testing implemention details makes your tests break for the wrong reasons. Ugh.
:/ Ever since I learned about the RTL ideology, using Enzyme has just been pretty miserable for me. It's so ez to test an implementation detail, find elements that also return the Component element rather than just the DOM element.
At work, I mostly see shallow rendering, expecting this shallow wrapper to match snapshot, expecting
wrapper.state('whatever')
to equal something... Don't really wanna start any crap so I try not to say anything.Also, I can't seem to find another reliable way in Enzyme to wait for an async
componentDidMount
besides returning the promise insidecomponentDidMount
and awaiting. I don't really like this approach though... because it relies on implementation detail.The frontend architect at my company endorses this pattern. So I'm pretty much forced to use this pattern since I can't use RTL
waitForElement
.Btw, looking forward to your talk at useReactNYC on Tuesday.
Sweet! Let me know who you are! Looking forward to meeting you.
Have you tried using Jest mocks to replicate shallow rendering? If not, maybe try suggesting this approach to your architect. See FAQ for more info.
Using Jest mocks is actually a more powerful approach anyways, since it allows you to explicitly choose which components to mock. With
shallow
, all components are mocked, even if they're defined in the same file.Yep. His concern was that it's too ez to not mock and then developers would put a bunch of tests into the component higher up in the tree, making it harder to maintain, whereas when shallow rendering just mocks as much as possible, encouraging to test whatever the current component does.
So far so good. I had one case however where I was unable to figure out one test case.
I have a button, that button has text. Upon clicking the button, the text gets replaced by a spinner. Thing is, the width of the button should stay the way it was when text was inside and not squeeze to be spinner sized. I did a simple render, put the width of the component in a variable and then fired a click event. I then tried getting the width and seeing if it matched. It was 0 all the time. I’m a bit new to testing but assumed JSDom or JestDom is being used when I render() the component and it would simulate properly. I even tried wrapping the button in a container div with preset dimensions. No luck still. For other unit tests it was a breeze. It really made me rethink how I test my Frontend.
I think JSDOM only provides DOM APIs. It isn't a full-blown rendering engine which is required to determine element's visual and structural properties. You'd have to use some visual testing tool to validate the behavior you described.
That’s what I concluded after tests but really wanted to test this case programmatically. I guess visual testing it has to be then.
You could check the styles or classes being applied at least and trust that the CSS is doing it's thing.
Yep, that's what I'd recommend. You could also use the
style
prop and verify the node.style.width is correct.Thanks, will try!
Would that really work in this case, i.e., when width is not specified by a CSS class?
With JSDOM, you can't measure layout, but you can verify that the style/class name was applied and trust that in the browser's ability to lay it out properly based on that.
Got it! Thanks! 😄
I love it! I constantly trying to push the library and thinking paradigm on everyone.
At the very least at my work, since they are still going to use Enzyme, I constantly am encouraging people to learn and research the paradigm (philosophy) that you have written about and the entire react-testing-library is built around... Ever since then, I have become the "go-to guy" for unit testing questions, I also have now done a few internal pieces of training. The tests have significantly improved, the coverage has increased and people have a better idea of WHY they are creating the tests just from shifting that mindset alone!
Really love RTL. We’re almost exclusively enzyme for frontend tests at work, but I’ve started introducing RTL in a few projects and everyone is really digging the design philosophy of it. Excited to keep using it going forward 👍
I love it and so does my team. I was an early adopter and introduced it to the team as an alternative approach to ReactTestUtils/snapshots. Testing components and workflows from the user's POV gives us enormous confidence and the ability to find elements using a11y tags kills two birds with one stone. We use it in conjunction with jest-dom and are over the moon with how reliable the products are.
The only real testing pain we've encountered has been lack of async
act
support but React 16.9 will fix this and we found plenty of useful workarounds thanks to conversations on the @testing-library/react GitHub page.So thank you Kent and all the project contributors. Long live @testing-library!
Love it
I wrote tests for almost two years with jasmine and karma for Angular project.
Now I'm working with react, react testing library and wallabyjs and tests is not pain anymore. For me it was huge improvements in my workflow and I feel much more productive.
It's the shit. My coworkers and I love it. I think enzyme, frankly, is weird as fuck (wtf is shallow rendering and what does .dive do? I don't get it).
Everything about react testing library so far has been really awesome. Easy to set up, easy to use, encourages testing UI the right way, and is just generally dope af. Much respect.
I really enjoy how we can just access elements in the DOM and how context and hooks just work.
I strongly agree with the guiding principles of RTL and have been using it for all new development that I can (specifically with the new Marko RTL wrapper). It has allowed us to catch severally A11Y issues already which is amazing since we already tried to avoid testing implementation details but making it easier to do goes a long way.
Having said that my current thinking is that for components there are really two people that need to be tested for. One is for the end user consuming your app, which is handled perfectly by RTL. The other of course is the consumer of the components.
In most frameworks the component consumer is interacting via props and events which are the public api for a component. To the end user it is an implementation detail, but it isn’t to the consumer. Having the ability to render and rerender components exists and is perfect for testing the prop API for components and there is usually some mechanism for testing events from the component such as spy’s. I definitely agree that accessing the component instance is not something the consumer would do and is an implementation detail, but testing props and events is and should be recommended to be tested.
All that is to say, I think RTL’s framework wrappers mostly handle that well, but I don’t feel like that clarification is made on the site. Primarily the wording appears to be about testing for the end user, which is great, but also the public api for components needs to be tested for the consumers of the component, which is often props and events emitted.
Unrelated, I have also been trying to reconcile the reasons why shallow rendering is so popular given the flaws that you mention in your article. I feel that your article highlights important points but might be a tad too absolute in its recommendation to avoid shallow rendering.
I think that using shallow rendering (especially with snapshots) does next to nothing as you say in terms of actually testing for the end user since 99% of the snapshot is testing implementation details. However I do think snapshots provide benefit to developers. They tell you when relevant sections of code has changed so that you can quickly and easily audit it, similar to comparing changes with git, except isolated to what an individual components renders.
However you could of course snapshot the entire rendered dom which would in fact give you more information about what was rendered, however I think at that point the level of noise is too much (similar to giant PR’s that no one wants to review) and so I think people opt for shallow rendering.
In Marko we are experimenting with a new technique which aims to snapshot the entire render tree while including minimal details about your child components. We are doing a full render of components (which means our equivalent of render props actually run which is nice!) and then are going to add the ability to clone the rendered DOM and strip out all nodes not owned by the component which you are testing. Meaning if you render a child component and pass children to it, only the children you passed would be in the snapshot, nothing rendered by the child.
And to end my list of ramblings, I feel that RTL (especially often being limited to JSDOM) is missing some tools to test as a user would, ie simulated scrolling, resizing, perhaps simpler tools for mouse and touch interactions as well? Perhaps good candidates for @testing-library/user-events.
Curious what your thoughts are on all this and thanks for your great work on this suite of tools!
Agreed! And I have a blog post on that here: Avoid the Test User. If you have ideas on how to improve the docs, please open issues/pull requests.
I'd like to add that many of the limitations are not React Testing Library's but actually JSDOM. If you want, you can use React Testing Library in the browser (with karma for example) and you avoid some of the issues you mentioned (though you get a few new ones).
And yes, user-event is the place for stuff like that 👍
Excellent, thanks for the link to the article - I hadn’t read that one. Sounds like we’re on the same page 🙂.
I certainly plan to contribute some more in the future!
I like the focus on testing via the DOM.
I found that my tests for form fields interactions made my React more accessible, since I decided to use Labels and aria-labelledby tags properly.
I also found that using title instead of data-testid led to more understandable buttons for the end user as well.
Overall, great work.
If I had even one suggestion, it would be to make the examples repo more interactive, perhaps with a demo or "playground" page where one can see the library in action.
I recently updated the repo to link to this: testing-library.com/docs/example-c...
Thanks!
What resources would you recommend for learning frontend unit testing? I've written unit tests in the past but now I'm planning on learning it properly. TIA. 🙂
My blog has tons of stuff. Search for "testing". Also, TestingJavaScript.com is a fantastic resource.
Awesome, thank you very much Kent.
Really like it. Haven't used Enzyme and I'm fairly new to React, but React Testing Library makes sense.
Being able to refactor a class component to a function component without changing a single test is also an encouraging experience.
RTL is amazing. The API feels so intuitive and easy to use, and it's not overly complex. Writing tests is a breeze now compared to other tools I've used...it's enabled me to go full on TDD and get crazy good test coverage across all the components using it. Great work Kent :)
I'm sure RTL has been a lot of work and I'm not trying to call you out. It seems you're really passionate about this and legitimately open to feedback.
I think it doesn't solve the biggest problems with testing. Testing should be easy and perhaps even fun. I've yet to ever once try to install RTL on a site and have it just work. Instead I get stuck in configuration land. It does little to improve mocking, which is one of the more frustrating parts of testing, especially for a beginner. Once going is it better than Enzyme? Sure, I guess. But only because it's opinionated and so you do less. I'd say RTL is acceptable but doesn't improve the testing landscape in a meaningful way overall. For those familiar with testing it's a nice little api that's moderately helpful.
I suppose this isn't the goal of RTL. The goal is to test the things that need testing, not implementation details. I think that's a worthy goal, it's just never going to be improved until testing is easy to do. RTL doesn't really move the needle on ease of use. As such it's a needless improvement in my view. You could have done the entire RTL task with a VSCode package that highlights or underlines when bad enzyme practices are being used.
The rtl is really nice when it works. But it seems have some serious problem when Material UI. I literarily spent more than 2 days still trying to figure out why the userEvent.type won't work on a properly selected input dom via testid.
Its very frustrating
Is there a way to simulate events in RTL like Enzyme’s .simulate()?
Yep! That would be
fireEvent
API.The docs have some good examples of how to trigger events.
Yes, though they're not simultated events. They're actual events like the ones the browser will fire when the user interacts with your component. Check it out here: testing-library.com/docs/dom-testi...
I do not like it.
I get random "should call act" errors depending on which device I run the test. If fireEvent.click needs to be wrapped in act() to be dependable, it should use it itself. Instead I have to wrap it myself every time. My test code is ugly and long-winded.
It promises interaction like the user does, but I can't get "the sort icon beside the column header". It has a function "within" but its use seems to be more for typecasting from HTMLElement to whatever internal type it uses rather than looking in the HTMLElement's children.
I have to step outside the framework to use .closest(..) to find elements "like a user would".
I have to add role, name, and other nonfunctional attributes to several places to my code because I cannot find by icon... because I'm not allowed to find by the class which puts the icon there. (FontAwesome, etc.)
I end up with tests that navigate by a mishmash of screenreader landmarks and visual landmarks, so I have no guarantee that either method of navigation works for either type of user.
I can't string the element, findBy, click calls in a fluent interface. The closest I get is with findBy(...) because I can string on .then(fireEvent.click). But this randomly throws the "act(...)" error depending on device, even when teh whole line has await. So instead I'm forced to write tests that read like a dick-and-jane English primer instead of a more natural style.
I can't get "the only select element on the popup" like a user would because I can't find by HTML tagname.
GetByText gets text from closed dropdowns and non-visible popups, making it largely useless. (Bootstrap3)
Getting an easily visible element by a combination of factors isn't possible. All my column headers have "click to filter" title, all my column headers' texts appear again in a dropdown or two, i actually want the sort icon beside the text in the header but there's one per column.... I can only get/find/query by a single criteria. I'm reduced to array indexes.
I like the idea behind this framework, but it's implementation leaves a lot to be desired.
I just started with @testingLibrary, i never thought testing will become this much simpler with all super cool minimal APIs. Thank you 3000.
I'm using at work and is helping us a lot!
I love it. I love writing tests from the perspective of the user, but sometimes I struggle when third party components are involved.
What are some of the "land mines" in enzyme that you try to steer people away from?
.instance()
.state()
,.setState()
,.props()
,.setProps()
)displayName
(like.find()
)Fundamentally, it's about testing implementation details, which enzyme enables and encourages. This leads to very poor tests which have many false positives and false negatives. Learn more from my blog post Testing Implementation Details.
I created an account here just to say: Enzyme…? Better to have called it "pile of shit stacked" 🤮, as you said: "looks like a minefield". Nothing works ... it's, awful, a waste of time using this fucking shit library. I have had a horrible experience using it. The authors would be better off dead.
The cypress testing library doesn’t feel like a cypress library.
Interesting. Are you saying that's a good thing, a bad thing, or simply an observation? Also, what makes you say that?
Does React Testing Library work with Apollo?
Yes
why the documentation is very much confusing?
I think peple who don't use semicolons in their library documentation should not be trusted.