All Articles

Mocking http responses locally using Node.js

JSON
Most common way of communication with backends: JSON

In the previous post, we added HTTP calls using the useEffect hook, so that our application can dynamically fetch the data from backend components. Naturally, this is required at some point or another - however, what do you do if that backend is not yet developed? Perhaps some additional complexity there has delayed their shipping of the product. Obviously, you do not want to be blocked by this!

And fortunately, you do not need to be blocked! As long as the API stands, you can already get cracking, develop your frontend, verify that every looks the way it is specified, or the way you want it to, at least.

You can test the logic of the application, you can already eliminate the first undefined kinks, you can eliminate the errors thrown by third party libraries, and so on.

What is mocking?

First of, I should technically use the word stubs here, since the mock server will be stubbing behaviour.

I will first go over the small distinction between mocking and stubbing. I am a big fan of both practices, as they add great value and speed of development when using clean TDD, Test driven development. They have slightly different uses though.

Stubbing

A stub is used, when you have a pre-defined object that you return in certain places. Examples of this could be abstract classes or HTTP responses. They live outside of your test however, which is a rather important point. A stub can/should be re-used when the same behavior is expected again.

As far as an application is concerned, it does NOT know that it is interacting with a stub - it may be the actual, expected object itself.

Mocking

A mock is created during the test itself (okay, it could technically be in some setup method, but then we’re already getting closer to stubbing). It is some object that is created to fulfill exactly the expectations you have at that point in time of the code. There are a multitude of mocking frameworks out there, such as Mockito or MockK.

Differences between mocking and stubbing

Most easily, the philosophical difference between the two can be explained as such:

  • Mocking: Initialization -> Expectation -> Exercise -> Verification
  • Stubbing: Initialization -> Exercise -> Verification

As you can see, during stubbing, you are not telling a framework what you are expecting of it - you are just running the program the normal way.

Custom mock server

Expectations

First, we need to define what we are expecting of our mock server.

Simply put, we want to have an endpoint to call when testing our application. But, hold on! Isn’t that what we did earlier, when we set up a response using Mocky?

Yes, it is exactly the same. However, there are some advantages when using your own mock-server:

Implement changes faster

Whenever you change the API, you can simply adapt your mocks, kill the server and spin it up again. No need to delete old mocks, replace the new ones, etc.

You can use the actual same endpoints

A hosted response provider is great for some quick testing, but in the end, the URL to call is always something cryptic, using UUIDs or similar. Obviously, they are not creating exactly the endpoint you want - how could they provide a bunch of different GET: /persons for example. When you control your own server, you can do anything you’d like.

Mocks will not disappear

If the provider has issues, you lose your internet connection, or they simply do cleanup on their side, you will lose all your mocks. Since the goal is to not be blocked, this hardly seems desirable.

Setting it up

The language of choice here is Node.js. I am using Node because it requires very little setup to get an application running. If we were to implement an actual backend where we had to define the endpoints, objects, etc., there would be a lot more effort required to handle a simple API change. With Node, we can quickly spin up a backend that just returns some JSON, which, after all, is the only requirement we have of it.

For starters, I have created a new node.js project using WebStorm. Now, we copy the response that we had earlier in mocky into a file responses/accounts/accounts.js with only a slight change:

module.exports = [{
    "id": 1,
    "amount": 320.8,
    "currency": "CHF",
    "name": "myNiceAccount",
    "description": "Wow, this is advancing!"
}, {
    "id": 2,
    "amount": 94.6,
    "currency": "EUR",
    "name": "eurozz"
}, {
    "id": 3,
    "amount": 420,
    "currency": "USD",
    "name": "Dollar account",
    "description": "The account of dollars I use for stock investments"
}, {
    "id": 4,
    "amount": 2600,
    "currency": "CHF",
    "name": "Savings",
    "description": "Savings account. Do NOT touch!"
}, {
    "id": 5,
    "amount": 5,
    "currency": "NOK",
    "name": "holiday money"
}];

Next, we set up the server. As a library, we will use json-server, which we can install by executing npm install json-server. After this, we can create the file server.js:

const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const accounts = require('./responses/accounts/accounts');

const PORT = process.env.PORT || 3001;

server.use(middlewares);

server.get('/accounts', (req, res) => {
    res.jsonp(accounts);
})

server.listen(PORT, () => {
    console.log('Server running at http://127.0.0.1:' + PORT);
})

Now we can run npm start and navigate to http://localhost:3001/accounts, where we’ll be greeted with exactly the same response again. It’s interesting though how quickly this was possible!

Integrate the mocks

Obviously, we now need to change the application to consume this mock. We can of course simply replace the URL for that request, but we can do better. We can set up the axios instance to go to the environment specific host per default, and then afterwards only specify the endpoint. This will make our frontend already portable throughout multiple environments, and will enable us to work locally, set up staging areas, and a production environment.

Let’s first create this environment specific properties file. On the root level of the application, create a file called .env (yes, it has to be called exactly that). In this file, you can add any properties that may differ per environment. IF you want more information about how to inject environment variables, how the priority is set for the different .env files, you can read about it here.

Also note that, in order for create-react-app to pick up any variables from this file, they must be prefixed with REACT_APP_.

Currently, we require this entry now for the mock server: REACT_APP_API_HOST=http://localhost:3001. Note that this may work also in production if the backend is deployed on the same, since there localhost will simply be the server itself. However, pay attention to the port, as that may be a different one by default.

Changing the axios instance is done by adding the following property:

  // default backend
  baseURL: `${process.env.REACT_APP_API_HOST}`

Now, finally, we change the URL in the AccountService:

class AccountService {
  static getAccounts = async () => {
    return await axiosInstance.get("/accounts")  // host is already set in the baseURL property of the acios instance
      .then((response: AxiosResponse<Array<Account>>) => {
        return response.data
      })
      .catch(() => {
        alert("Unable to retrieve the accounts.");
        return [];
      });
  }
}

If you run the application anew, nothing will have changed, which is kind of the goal. Now you can add as many additional accounts to your request without bloating your code. Pretty neat!