Testing React connected functional components with typescript

I was never fan of tests on front-end, but fortunately it changed over time. I see big value in having automated tests regarding functionality of components. By that I mean checking if our state is correctly updated and our functions and requests are executed in proper sequence. I don't like checking exact rendering style (except that component can render without error), so testing snapshots or checking positions is not for me. 

Starting with React I was fond of functional components and their hooks, and still I prefer it over class components. When I started testing there always seems to be a lot of problems and questions about how to check state of functional component or how to check if our redux store has been updated. Searching through web I've seen many topics and not answered questions, but after some time I managed to find good hybrid solution - connecting hints from many repositories and discussions. That's why I wanted to share it, to provide one source for fully testing functional components and I hope it can help someone in his fight for better code...

My component

I prepared simple component form with 3 fields. It contains my custom implementation of @material-ui components. In fields you can select bike brand, type and color (2 single selects and 1 radio buttons). Button next is saving data to redux and is inactive until all fields are filled. Each time data in field is changed, it clears fields located below and executes fetching of fields options (ie if you select brand like Kross it can have limited bike types and colors, which can not be available in Specialized bikes). We don't have back-end here, so url for fetchning bike options is faulty, which is not  stopping us from testing it. I tried to use a lot of variants of useEffects and setStates, just to present test functionality. There are some comments included in whole code, to point important places.

 Unifying tests

Common container

To ensure that all of my tests would use the same configuration I created common container. Here I needed only redux provider, but in case you use ie translation or any other libraries, which must be run along with your app, it is much easier to make it once and develop once any configuration or library changes.
Here I also made override for browser window variable, because sometimes they are used in components. In my example I'm setting window.location.origin (not used here, but can be handy) and window.alert (I use it in normal application to show saved state).


Setting up store - what with state ?

As you probably know redux-mock-store does not implement reducers, so our actions can't update state. Then it is real problem to properly test your component and updating component state, ie when it is updated after change of value in store.
Fortunately there is the solution. Normally in MockStoreCreator we simply put our test state and in configuration we're not setting any middleware. 
First thing is of course setting middleware (in my case thunk), so our actions can be properly dispatched. Then we can use a "trick" and instead of putting state in creator, we can make it function, which will call at the end reduce method. Below I present my common test store configuration, in which we can also put some common state, if it is needed to properly run our components:


Building a store in test file is simple, we just create our initial state and execute method to create our test store:


Async useEffect - how to make it work?

useEffect is asynchronous function, which causes a lot of trouble when testing. Many developers don't know, but this function has it's synchronous equivalent called useLayoutEffect. For project components I wouldn't recommend to overuse it, but in case of tests it comes as redemption.
Now we have only to create __mocks__  directory and in reacj.js put mocked import from React:


Async request - can we wait for it ?

Answer is simple - yes we can. With help of setImmediate, which runs callback functions immediately, we can just await in test, for our promise to end.


Is action fired - how to test ?

Our test mockStore is providing getActions  method, which we can use to check what actions were dispatched and what was they're type and data. That's why, to not duplicate code, I prepared common method using deepEqual method:


Mocking requests

One of the most common topics is mocking request. We can use there jest.mock and then use method mockReturnValue on our method:


We can test !

Now we can simply write our testing scenarios using act method of react-test-renderer. Only thing which we must remember is to put every action in separate act method and always await for result of it. So our test rendered component can update up to our checking part. Below you can find one of my test. Whole described here examples and working code you can find on: https://github.com/novakDev/react-full-testing


Comments