React Testing: Mock API Calls
React testing can be quite a hassle. Positive-negative functional tests, especially those with multiple conditional renders can be demotivating. Not to mention dealing with a LOT of asynchronous calls, which I personally still struggle with (Yes, race conditions exist, and its a pain).
Another big bump I stumbled upon was to test methods and/or logics that involve calling an API and returning the response in somewhat non-explicit ways, like updating a hook state or updating fields that correspond to the API caller. You might think that API calls are just normal async calls. Well, you’re half right. While it is possible to simulate async calls with Promises, it isn't feasible to call the API directly while testing. Remember that the testing stage in a CI pipeline should be done as quickly as possible. And while it is easy to depend on a fast API server, it is still ugly, prone to unexpected error responses, and have a potential to crowd production database connected to the endpoints with test data.
What is mocking, and why mock?
Mocking is basically just a simulation. In our case, it simulates real API behavior in terms of their responses that matches certain requests. Why? To get quick and consistent responses. Let’s see some examples from my PPL coursework.
TBCare case filter/search (mobile)
TBCare has services to do searches based on TBC cases attribute values. Here I have a field for users to enter search queries for TBC monitoring cases. Notice the updateValue has a callback to set the searchQuery state.
And here I’ve implemented a function that calls the service function to do the API calls to our endpoints. Notice that the function only gets called within a 500ms timeout delay on an update on the searchQuery to avoid spam calls.
Now how do we test this? First of all, our requests are done with axios. So it is wise to do our testing with axios rather than fetch. Our group uses jest for testing, so might as well also use it to mock my axios requests.
Now I have my mock object, I am going to create a mocked response function to return the expected response. In this case, the expected response is an array of monitoring cases.
Next up, the test function! The first thing to do is to set the mock object resolved value to the predefined response data above. And then I rendered the whole component, so I can access the search field through the root attribute. After getting the search field, I then updated the value to a case name and then expects the response to match the predefined data (Which it will because I mocked it!). Notice I put a 500ms delay, to take into account the delay on the useEffect method that calls the API before. And then finally, expect the mock request to be actually called after updating the search field value.
And it’s done! I have simulated an API request without actually calling the API server with mocking. That is all for this article, I hope it is useful in some way. Have a good day!
Written as an assignment for my software engineering project individual review.