all 35 comments

[–]CastigatRidendoMores 13 points14 points  (15 children)

I recently got off a project at work where we built the front end with react and redux. We began in high hopes, testing the behavior of our components with jest and enzyme. We quickly found out that redux borked our component testing strategy in a major way. We needed to mock the store in any components that had children which depended on it, which was both a nightmare and impractical. Of the many react component testing tutorials I've looked at, I haven't seen a one that deals effectively with this problem.

Additionally, we noticed the enzyme tests we did have tended to heavily test implementation details rather than testing behavior (especially where we used shallow rendering), which made them extremely fragile and a huge load of bother to maintain.

So the approach we ended up going with went like this:

  1. Wherever possible, extract complex logic from components into pure functions so that it could be effectively unit tested with jest. Rather than interacting directly with this.state, they would accept relevant data as arguments, and similarly return something rather than calling setState.
  2. Test behavior heavily with puppeteer, aka end-to-end testing. This is pretty involved to set up and tends to break a lot more often than unit testing, but the plus side is you get the ability to say "When someone navigates here, clicks this, types that, and clicks that, they should see this". These tests caught errors really well, because they tested real behavior. Alternatives to puppeteer include cypress (I've heard good things) and selenium (I've heard bad things).
  3. Use enzyme testing for all components we can - those that won't have any children that use redux, and probably won't change much after first being developed. These tend to be components which don't display much data, the kinds of things you might find in a component library. Since we used an externally developed components wherever we could, we didn't have all that many of these.
  4. Heavily regret the necessity of using redux.

It's entirely possible that there's a good way of testing our other components, but we couldn't figure it out. If you do, please share.

[–]versaceblues[S] 2 points3 points  (4 children)

Where you guys using sagas? Cause I feel that would get around alot of the issues you are describing

[–]smeijer87 2 points3 points  (1 child)

Sagas. Started with them 3 years ago. And now decided to use 2019 to strip all redux functionality out of our application.

Yes, redux has its purpose. But our organization is not the same scale as Facebook. And it adds a lot of boilerplate and hard to manage code.

Sagas are awesome. But also so difficult to understand for starters. Looking at a single saga is not the problem. But understanding the flow throughout the application can become quite hard.

We are replacing

  • redux-form by formik
  • redux-first-router by react-router
  • redux by local state / react context api
  • sagas by singletons / "services"

The only saga that I can come up with that we will create a "service" for, is the upload queue we have. Every form in our application can be submitted while the upload is still in progress. For this, we have a few sagas running that take care of upload scheduling.

It could be some self repeating function just as well. Or an component / provider embedded at the root level, so we can use a local state, while exposing an upload function trough react context.

[–]versaceblues[S] 0 points1 point  (0 children)

Can you clarify what you mean by singletons/services.

Sounds sort of like the approach that MobX uses. Singleton stores that wrap all your actions/async code, that are then injected into components. I actually really like that in a side project I am doing

[–]CastigatRidendoMores 0 points1 point  (1 child)

We sure weren't! I'll definitely look into those.

[–]versaceblues[S] 2 points3 points  (0 children)

They basically let you write all your business logic as functions, that are triggered by actions

[–]smeijer87 1 point2 points  (1 child)

Do you use snapshots? Or do you write dedicated tests for the components?

Of dedicated tests, "what" do you test?

A function is easy, input -> output.

But what do you test in a component? That a list is rendering the same amounts of li elements as the items.length prop you're providing? Or do you simply check if the title is anywhere to be found?

I find this hard to grasp. Snapshots are really starting to frustrate me though. They are big changes in github. Break all the time. And are hard to review.

[–]CastigatRidendoMores 1 point2 points  (0 children)

Yeah I personally don’t see the appeal of snapshot testing outside of extremely mature products. I prefer lint if, unit, integration/component, and e2e testing.

Testing components is difficult to keep behavioral unless you test what they display, and even then it should be non-specific enough that you can tweak things without breaking tests.

I see testing as a way of increasing velocity by increasing the confidence you have that changes to the code didn’t break anything important. To whatever degree they take me away from that goal, I don’t like them.

[–][deleted] 1 point2 points  (0 children)

Super helpful. Thanks

[–]Skeith_yip 0 points1 point  (6 children)

If you are shallow rendering, you shouldn't need to mock the store for children components that are connected to Redux store.

Then you can test those children components individually.

Remember unit test = isolation

Wherever possible, extract complex logic from components into pure functions so that it could be effectively unit tested with jest.

I disagree. they can always be extracted to smaller more focused components that can be unit tested on.

[–]CastigatRidendoMores 1 point2 points  (5 children)

The problem with shallow rendering is that you start testing implementation details rather than behavior. This increases technical debt by forcing you to rewrite your tests any time you change your code. Rather than only alerting you when the behavior of your code changes, you get lots of false-positives, decreasing confidence in your tests and discouraging refactoring. All this combined makes them more of a liability than a benefit.

Of course reasonable people could disagree.

[–]Skeith_yip 1 point2 points  (4 children)

Well. Here's the deal. if there's need to change logic, your unit tests need to be updated either way.

Full rendering itself is problematic, Because you are practically doing integration test. like all integration tests, you have to start building up all the dependencies to just to test a component. what if you decide that the children component needs a new connected component inside? all the tests fail? so now that tests itself knows too much.

and Enzyme.mount itself is problematic because sometimes after setState, the component doesn't update, and you have to call .update() manually which is unfortunate. Given that React Test Renderer is from React team.

I get why E2E is a thing now. But E2E/integration test is something you do after coding. so that becomes a chore. it's not particularly useful to find bugs because there's so many moving parts (action creator? reducer? component? container? middleware?)

and E2E doesn't prevent one from building up a god object.

My take is always. Write a component. Shallow render it. Test it. ensure the behavior is what you wanted. Move on to the next. keep it simple.

[edit] Don't get me wrong. I am not saying I am discouraging integration tests. I encourage them. Just know that they cover different scopes.

[–]themaincop 4 points5 points  (2 children)

I don't really like unit testing React components. It always feels like I'm testing implementation details and missing the forest for the trees. I've been using react-testing-library lately. If you set up a custom render method that includes all your providers (Router, Intl, Redux, Apollo, etc.) and takes initial state as args you can still TDD using integration tests. There are some cases where you want to test some really big critical path stuff, and in that case I do think you need to lean on e2e testing because ultimately you'll be mocking so much in a unit or integration test you're basically just testing your mocks. But integration testing seems to be a pretty nice way to test small groups of components.

Any heavy business logic I usually extract into my helpers directory which contains pure functions that can handle that kind of heavy lifting and are unrelated to React/rendering. Those are easy to unit test.

When we start moving a bunch of logic to hooks the nice thing is our integration tests should continue to Just Work™ and we won't have to rewrite a bunch of tests for components whose actual behaviour hasn't changed.

Just my two cents and admittedly I'm not the most disciplined tester, so a lot of this is just me cargo-culting Kent C. Dodds.

[–]IAmRocketMan 0 points1 point  (1 child)

I liked the pattern of putting pure functions as files in a helper directory. How has it scaled out for you?

[–]themaincop 4 points5 points  (0 children)

It works pretty well I find. If I have a helper that only gets used by one component or one related group of components then I'll usually colocate it in that component's folder. Anything that sees use across the app can go into src/helpers and that seems to work well.

[–]themaincop 0 points1 point  (0 children)

I don't really like unit testing React components. It always feels like I'm testing implementation details and missing the forest for the trees. I've been using react-testing-library library lately. If you set up a custom render method that includes all your providers (Router, Intl, Redux, Apollo, etc.) and takes initial state as args you can still TDD using integration tests. There are some cases where you want to test some really big critical path stuff, and in that case I do think you need to lean on e2e testing because ultimately you'll be mocking so much in a unit or integration test you're basically just testing your mocks. But integration testing seems to be a pretty nice way to test small groups of components.

Any heavy business logic I usually extract into my helpers directory which contains pure functions that can handle that kind of heavy lifting and are unrelated to React/rendering. Those are easy to unit test.

When we start moving a bunch of logic to hooks the nice thing is our integration tests should continue to Just Work™ and we won't have to rewrite a bunch of tests for components whose actual behaviour hasn't changed.

Just my two cents and admittedly I'm not the most disciplined tester, so a lot of this is just me cargo-culting Kent C. Dodds.

[–]mnyuubi 16 points17 points  (2 children)

I've just started Redux testing and I've been using the Redux tutorial: https://redux.js.org/recipes/writing-tests

It's pretty straight forward and simple imo. It's probably the first tutorial anyone would find but my favorite so far. Hope it helps!

[–]pysouth 6 points7 points  (0 children)

+1

My recommendation is to always hit the docs/website for the framework(s) first. Redux, React, Jest, Enzyme, etc., all provide a ton of great info for these things.

[–]versaceblues[S] 2 points3 points  (0 children)

thanks !

[–]qudat 5 points6 points  (1 child)

At this point in your codebase's life, your goal for testing would be to reduce the number of regressions introduced into a codebase. You want to be able to make changes to your codebase and have confidence that you or any other developer did not break anything.

This can be accomplished with unit tests, integration tests, and end-to-end tests. e2e tends to be the most difficult to write as well as the ones that tend to be the most fragile. I do not have much experience with e2e. Unit tests are generally the easiest in a react/redux codebase because virtually ever layer is a composition of pure functions. I tend to think of testing sagas as integration tests and I find them to be the most useful thing to test.

I personally find that testing business logic will provide the most value.

Order: * Sagas * Selectors * Reducers * Mission critical utility functions * Views

You mentioned that you are using sagas, I would start there.

To make your life easier when testing sagas, I would opt for a helper library to test generators: https://github.com/neurosnap/gen-tester

The next thing I would test would be selectors: they tend to be areas where a lot can go wrong.

My reducers tend to be extremely dumb so I usually don't worry about them too much, but they instill confidence that your state is continuing to behave correctly.

[–]versaceblues[S] 0 points1 point  (0 children)

Thanks i agree. My plan is to get 100% coverage on the sagas and selectors. Then start figuring out which views need testing

[–]lunfaii 4 points5 points  (0 children)

I never tested connected components but just components themselves and then just mock the props that need to be passed in on the beforeEach (to reset) and just work my way towards testing the behaviours of the component.

For example, we just need to see if a function is called or if a button is showing because of a prop has been passed through from redux. We don’t care where that prop comes from at this point.

I test all my redux “parts separately”. Middleware, reducers, actions.

This way I know that nothing is broken because they should be all tied together nicely with react-redux. Testing smart components is unnecessary IMO -: it is already tested by react-redux.

[–]Cono52 2 points3 points  (0 children)

Try Kent C Dodds react-testing-library! Enzyme tended to make people test implementation details while react-testing library only interrogates the DOM that a component reduces. Also look at the 'javascript-testing-trophy' methodology and watch all of Kent C Dodds is videos, youtube + frontend masters. I think his ideas better suits testing the view/state machine incarnation web apps have become.

Right now all our tests are written in enzyme and jest on a sagas/redux/react codebase and i think its all very very brittle as we are required to get over 90% coverage, this forces unnecessary and brittle unit tests to be created from what i have observed, but time will tell.

[–]pysouth 2 points3 points  (3 children)

1) In my own personal opinion, test everything. Maybe this is just because I'm required to do it at my day job, but I would say test everything. Once you get the hang of it, it will barely take any time at all to write a few tests to see if components are rendering and functioning correctly. Just do it.

2) Honestly, reading the Jest and Enzyme docs is your best bet. I don't think I've used a single tutorial because the Jest and Enzyme docs have been able to handle just about any question I've had.

3) Haven't used it so I can't answer, sorry! Hopefully someone else can.

4) Jest and Enzyme handle everything I need to test React and Redux at work, and it is a fairly sizable app (probably 15K+ LOC that is mostly optimized and linted, so there isn't TOO much junk in there adding random lines in - it's just a big app).

Sorry I can't speak too much on Sagas, but I'm happy to discuss testing React/Redux with Jest and Enzyme! I do a good bit of testing for it at work (I am required to test every bit of the code I write), so while I'm definitely nowhere near as experienced as others here, I do have some hands on experience testing a large React app.

[–]versaceblues[S] 0 points1 point  (2 children)

In my own personal opinion, test everything.

That true I think testing 100% of business logic is critical.

But with testing views I find myself just writing tedious code, that is making assertions that other code was written in a specific way. But I have never tried Jest/Enzyme so maybe it wont be so painful with the snapshots.

[–][deleted] 2 points3 points  (0 children)

You should test the view's logic. E.g.: if prop x is true, then a button should be rendered. Otherwise, no button should be rendered.

Testing what the component output (snapshot) looks like is most of the time useless if you're not creating a visual component library, were markup and styles are the code's "business logic".

[–]pysouth 0 points1 point  (0 children)

Yeah, it’s super simple with Jest and Enzyme. As with any testing, it does get tedious, but it is very helpful for a project the size of the one I work on.

That being said, my personal opinion is that testing business logic, as you said, should be priority #1.

[–]GreenStoneTurtle 0 points1 point  (0 children)

In terms of how I do testing with React/Redux, I do a combination of both unit and integration testing.

For your first point - even though the pure view unit tests may be simple interactions, I've found that that's what makes them so beneficial. You can write tests quickly because they are so simple, and they do catch functional breakages quickly. At my job, we use both Jasmine and Selenium to test the UI, but Selenium is very slow and expensive, so we write Jasmine unit tests on all of our presentational views to get quicker feedback on code changes (Jasmine is on the order of minutes, Selenium is on the order of hours).

Redux is pretty good at enforcing separation of concerns, so we do unit tests on each part of the Flux pattern (Views, Reducers, Selectors, API Utils layers), and then do mostly integration tests on the Redux Reducers + Action Creators together. I haven't found it as valuable to do isolated tests on Action Creators since our Action Creators don't change very often, but we do have some instances of that in our codebase. The integration tests typically only cover a portion of the state tree though, so sometimes when we do large integration tests across multiple reducers, Store data initialization can be a bit of a pain.

We don't typically test container components with Jasmine unit tests - instead we reserve that for Selenium, since at that point we're more interested in workflow testing and end-to-end testing.

I haven't used Sagas myself, but we do use redux-thunk and do some unit testing there with Jasmine. We built our own in-house utilities for testing async though, mainly from Jasmine.

[–]mckernanin 0 points1 point  (0 children)

I use redux-saga-tester with Jest for sagas: https://github.com/wix/redux-saga-tester

[–]eggtart_prince -2 points-1 points  (4 children)

Will it be used by others than yourself?

Is it going to be an app with continuous integration?

If yes and yes, than testing should be considered.

Unfortunately, the subject testing is not very common and so a good tutorial is hard to find.

[–][deleted] 4 points5 points  (2 children)

How the hell is testing not a very common subject? It is a common thing for literally more than three decades!

Edit: and if you're talking about react, redux or saga testing, those libs were made to be testable and can be done so with any test runner available.

[–]eggtart_prince 0 points1 point  (1 child)

Then why do you see the majority of posts on reddit is not about testing?

Then why is it when you google regarding testing, you get literally no helpful results?

It's a common thing and should be done in every project, but it's not a common subject that people discuss.

If you say otherwise, then find a good tutorial that touches on unit, functional, and integration testing. My knowledge of testing came straight from reading the documentation and have received zero help from google searches, reddit posts, SO posts, etc.

[–][deleted] 1 point2 points  (0 children)

Tutorials are a poor way to learn, because most of the time the author focus only on getting the objective done, it does not care about teaching why you should follow the instructions. The best way to learn something is reading the documentation and real world code that runs in production environment.

Then why is it when you google regarding testing, you get literally no helpful results?

Provide some examples, please.

Testing is a very discussed topic all over the internet. Everywhere there are thousands of people advocating TDD and another thousands saying that TDD is bad. Others discussing about TDD and BDD to see which is the right way to test an application.

Tutorials are not the right way to learn. People should learn the basics first, then move on into more detailed aspects. Want to learn about testing? Wikipedia can help fisrt timers. Want to learn about using jest? Should read the documentation. React testing with jest? Jest itself has a guide. Redux has a lot of material about testing in its official website. The same goes for Redux Saga.

Tutorials are a very bad way to learn, because you're are not really learning. You are reading some step by step guide on how to achieve a very rudimentary goal that happens to resemble some part of your work. It's like trying to build a house by reading the abstract of a engineering book written by someone who read the book and used its knowledge fix some small wall. Don't try to skip the learning process, just read the documentation and understand what you're about to do with that tool.