How to mock the dynamic split join pattern
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Pain Free Mocking with Jest course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Pain Free Mocking with Jest, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
[00:00 - 00:11] In this lesson, let's review an important design pattern known as dynamic split join. It comes up frequently when building clients that depend on REST APIs and have to make multiple asynchronous calls.
[00:12 - 00:27] We will test a service function and learn how you can mock requests that use this pattern. Currently, what we have is when the GET recipe route is invoked, we make a request to GET recipes that contain the given ingredients and return same to the client.
[00:28 - 00:36] Now we want to add nutrition data to each recipe returned earlier. We need to make a follow-up request to the nutrition API.
[00:37 - 00:53] This follow-up request is known as an enrichment call, where adding meta data to the existing data. In my professional experience, I have used this pattern while creating e-else connectivity solutions, where in a district's dashboard you have to make an enrichment request after getting the initial district data.
[00:54 - 01:04] The enrichment request could be to get information about laboratories, health diagnostic devices in different laboratories, etc. So how do you approach solving this type of problem?
[01:05 - 01:12] And most importantly, as far as this course is concerned, how do you test it? Let's look at our example implementations.
[01:13 - 01:35] In our GET recipe route controller, we query the recipe API in the controller using GET recipes by ingredient, and we then loop through and make individual enrichment calls directly in the controller to fetch nutritional data for each recipe. The problem with this approach is it tightly copies the logic and makes our controller bloated.
[01:36 - 01:59] A better approach will be to split the logic into reusable service functions. And in our second implementation, we have our GET recipe with nutrition function, which queries the recipe API and then makes the enrichment calls to get the nutrition data merges the nutrition data into the earlier returned recipes and we then return the same.
[02:00 - 02:07] As a best practice, you want to modularize your application. Look at separating your controller from your services and your route handlers.
[02:08 - 02:12] This is what it's testing. Let's look at the GET recipe with nutrition function.
[02:13 - 02:51] GET recipe with nutrition function basically gets the list of recipes that contain our ingredient and then we loop through the returned data using the map function and for each recipe, we call the GET recipe nutrition promise, which returns a promise and we are then left with an array of promises, which we then use promise.org to get all the promises to resolve and we return the value. In the GET recipe nutrition promise, this is the function that actually makes the call to get the nutrition, but this function itself returns a promise.
[02:52 - 03:00] One point I would like to touch on is the environment variable. Looking at this, we are reading our rapid API key from the environment variable .
[03:01 - 03:19] You want to make sure that you don't check in your environment variable file into GET and how we did that was in the GET ignore, we made sure that we are not tracking our .10 files. Another thing we also did is to include the sample.env.
[03:20 - 03:29] This is for documentation and quick onboarding. Now let's look at how we will test this HTTP endpoint that uses the dynamic split join pattern.
[03:30 - 03:42] Now let's look at how you will test the HTTP endpoint that uses this dynamic split join pattern. The first thing I did was to bring in all our HTTP endpoint tests for logging route and sign up route.
[03:43 - 04:00] What is of interest to us is the recipe route for now. And as usual, we use the orange act as a set pattern in writing our tests and in the orange step, I set up NOC to intercept the request to our recipe API and I determined what it should return, which is the pasta recipe list.
[04:01 - 04:06] You must have seen this in previous lessons. And here we called our create request interceptor helper function.
[04:07 - 04:13] What this function does is this, our pasta recipe list is made up of 10 objects . Each object is a recipe.
[04:14 - 04:32] You can see the title ingredient sevens and in the nutrition list, we have arrays nested inside this nutrition list array. And for each item in this array, you have different objects, which is the nutrition object that matches the index in pasta recipe list.
[04:33 - 04:45] So for example, this recipe, MRL P pasta at index zero in the pasta recipe list . Well, you have the nutrition data in the index zero array in the nutrition list .
[04:46 - 05:01] So you have the P and you have the pasta. Similarly, say, for example, the second item, which is a cream sauce in nutrition list, the second item also will be nutrition data for cream sauce, so on and so forth.
[05:02 - 05:23] And instead of having to set up NOC with 10 different calls to intercept and return the object 10 times, we can leverage the helper function to look through the recipe list and then get the nutrition based on the index. Going back to my tests, over here, we're assessing that it should be successful .
[05:24 - 05:28] We should return 200 and that's as well. We did here using super test request against our server.
[05:29 - 05:39] And this is the HTTP endpoint. The other test, what we are interested in is that in each of the recipe objects returned, it contains a nested nutrition object.
[05:40 - 05:53] We set up NOC and we used our helper function here to create 10 different NOC setups. We then made a request to the API endpoint and asserted that it should return status 200.
[05:54 - 06:07] And lastly, we get a random item in the list of recipes and asserted that it should contain nutrition. The last test that we have is similar to the previous tour here.
[06:08 - 06:24] We just want to be sure that the data it returns contains the recipe and the nutrition is not merged in one object and you've seen this already in our setup. The only thing that is different here is we used another helper function, match list.
[06:25 - 06:39] And what this function does is to look through the recipe list, then get the nutrition and add the nutrition object to the recipe object. We made a request and we asserted that the response must be 200.
[06:40 - 06:50] And based on the match list that we created earlier, we asserted that data returned in the response must be equal to the match list. I hope this example helps you understand the dynamic split-join pattern.
[06:51 - 07:03] You can read this article on Medium to better understand the dynamic splits pattern. It also discusses different other types of API client patterns that you'll find interesting and informative.
[07:04 - 07:09] In the next lesson, we'll look at an edge case and how we can mock a slow network. During the next lesson,