3 ms·
In the small-med biz teams I worked in, we didn't try to write component-level tests. We had a bunch of unit tests for basic utility functions, API calls, etc.,
by solardev 2y ago
In the small-med biz teams I worked in, we didn't try to write component-level tests. We had a bunch of unit tests for basic utility functions, API calls, etc., but then to test the overall functionality of the frontend we just wrote a lot of frontend & E2E tests with Playwright/Puppeteer (or a cloud version, like https://docs.datadoghq.com/synthetics/ https://docs.datadoghq.com/synthetics/ or https://www.browserstack.com/percy https://www.browserstack.com/percy).
Those are just real browsers running headlessly, performing UI actions, and then verifying the results (either looking for something in a real DOM and/or comparing screenshots). If something went wrong, the devs can then follow the exact steps to replicate it and debug it.
It was a lot quicker (and arguably more useful, I think, since it emulates real users and mostly real systems -- if your staging setup is similar to prod) than trying to shim some intermediary component testing. (You can also fake certain inputs in lieu of waiting for a backend, if you prefer.) Is it perfect? No, but it's a quick and cheap stopgap.
Edit: A further argument is that this can also help catch unexpected DOM bugs (like z-indexes or viewport width weirdnesses or plain browser incompatibilities) that pure code bugs won't catch. At the end of the day it's what the user sees that matters, so IMO if you have limited resources and can't write tests at every level, it's better to have more E2E coverage that realistically emulates what a real user would experience, since that would typically cover logic bugs too (at least happy path ones).
- PaulHoule 2y agoI've worked in teams that had testers who used commercial tools to run regression tests against real UIs and found that to be helpful. As you point out there are certain things that are easy to test. The other day I wrote a paginator using a TDD approach because I knew roughly what behavior I wanted but wasn't sure how to implement it. I wrote a simple function that took a few arguments and returned an array like [0, 1, 2, -1, 10, 11, 12, 13, 14, -1, 26, 27, 28] which made it really easy to test the inputs and outputs. I didn't think the code that generated the HTML from that was really worth testing because it was so superficial.
- solardev 2y ago> [0, 1, 2, -1, 10, 11, 12, 13, 14, -1, 26, 27, 28] What is that supposed to represent in terms of pagination? Are they cursors...? > I didn't think the code that generated the HTML from that was really worth testing because it was so superficial. For what it's worth (as a frontend person), pagination is traditionally one of the trickier things for us to make sure is working correctly. Off-by-one mistakes can happen for example (especially with zero-indexed items). Or sometimes the prev/next buttons don't correctly use the same math as the page 1, 2, 3, etc. buttons. It gets even harder if you don't know the total length in the beginning, or if you allow multiple page sizes or sorts/filters, or use lazy loading, etc. For those reasons I try to use a ready-built lib like MUI where all that is already tested internally (like https://github.com/mui/material-ui/blob/next/packages/mui-material/src/usePagination/usePagination.test.js https://github.com/mui/material-ui/blob/next/packages/mui-ma... or https://github.com/mui/material-ui/blob/next/packages/mui-base/src/TablePagination/TablePagination.test.tsx https://github.com/mui/material-ui/blob/next/packages/mui-ba...), but we still add our own automated and manual tests in our own usages. But then again I'm bad at math and division, lol, so maybe it's just my own weakness.
- PaulHoule 2y agoThe paginator was one of the sources of stress in that project, it actually had me walk away from the keyboard and think about it a lot before deciding how to do it. Non-negative numbers are the index of the page starting at 0 and -1 is an ellipsis. This particular system in written in Python with Flask, I am using this CSS https://getbootstrap.com/docs/5.3/components/pagination/ https://getbootstrap.com/docs/5.3/components/pagination/ so 12 gets rewritten to <li class="page-item"><a class="page-link" href="?skip=12">12</a></li> As you point out this is the kind of code that has devilish complexity in the corner cases (benefits from having tests) but is also easy to test (no need for mocks, callback engineering, etc.) Some other examples like this are almost any kind of string parsing where off-by-one and other silly errors are so easy to make and where tests are so easy to write. Now you could also test the function that writes the HTML for "12" and it is pretty easy to do but the gain is small and it is also un-DRY because if you want to change the appearance of your HTML you have to change the test. (Granted, a good set of unit tests could prove that it's hard to sneak an HTML/JS injection in.) In the early 2000s there was a huge controversy over Mock Objects in Java testing. Back then you had to write your own Mocks so it was a lot of work which could be just as error prone as your own code. At the time it was also easy to get into test induced design deformation sneaking your mocks into code. 10 years or so later there was Mockito and also advances in dependency injection that made mocks ergonomic in Java in most cases.
- morbicer 2y agoExactly this. Unless your product is components (e.g. you are in a team building UI kit) it's better to have unit tests for util functions and integration/e2e tests in Playwright or Cypress. We try to avoid logic directly in components so apart from few exceptions (like complex tree pickers) it's moot to test them. For these few exceptions RTL serves us pretty well. Testing redux reducers was cleaner back in time but hooks can be tested with renderHook/act and that's where your logic should be.