Fetch API using OOP with unit test

In this article we will cover an example of Object Oriented approach on using the Fetch API to let us make HTTP requests and process the response using a given function.

This is more like an exercise to get a better understanding on coding standards in a professional environment using a simple use case.

It includes the following features :

  • use of fetch API
  • OOP
  • ES6+ module
  • Jest for unit testing
  • Mockbin for live demo

Following a comparison chart on stackabuse, we decided to go with the Jest framework in order to test our functionality.

We will try to keep things simple while using the XMLHTTPRequest replacement alternative implementing a Promise (we will cover this topic in another tutorial). Take a look at the documentation on Mozilla about the fetch API.

Pre requesites

For the sake of this tutorial, I’m using Node, I assume that you’ve got a set up ready on your machine.

You will then need to install the node-fetch package as it isn’t available by default (see npm) :

$ npm install node-fetch

And because we are professionals (:)), we will implement tests to cover our features !

Please follow the requirements for using Jest.

Basically you will need the following packages :

  • Babel
  • Jest

Codebase

The whole code base is available on GitHub, you may clone the repository or simply copy/paste the code snippets.

Let’s begin with the business part featuring the Fetch API (NetUtils.mjs) :

/**
 * Object handling GET & POST request passing a URL as parameter
 * and treating the response via an external function
 * Unit test provided
 */
class NetUtils {
  constructor (url, method, headers, action, fetch) {
    this.url = url
    this.action = action
    this.initParams = {
      method: method,
      headers: headers,
      mode: 'cors',
      cache: 'default'
    }

    this.fetch = fetch
    this.request = new this.fetch.Request(this.url, this.initParams)
  }

  proceed () {
    this.fetch(this.request)
      .then(this.action)
      .catch(function (error) {
        console.error('Error occured : ', error.message)
      })
  }
}

export { NetUtils }

Note that I’m not using the async/await ES2017 functions as mentioned here. The code is pretty clean according to me but in a more complex context I advice you to implement the last functions for a better reading.

The above class is more like a wrapper object for the Fetch API.

Calling NetUtils requires us to inject the Fetch Object and a few parameters (url, method, headers) as well as an action to achieve once the API has done its job successfully.

Now the main entry of the program (main.mjs). I’ve decided to add the mjs extension so I can use it from within a browser without the need of defining the script as a module.

import fetch from 'node-fetch'
import { NetUtils } from './NetUtils.mjs'

const myHeaders = {
  cookie: 'foo=bar; bar=baz',
  'x-pretty-print': '2'
}

const netTest = new NetUtils('http://mockbin.com/request?foo=bar&foo=baz', 'GET', myHeaders, myAction, fetch)

function myAction (response) {
  console.log('Only printing the resulting status of the call : ', response.statusText)
}

netTest.proceed()

console.log('Waiting response...')

As we are using modules in a Node environment, prepare the following configuration files (follow the guide about Jest and ES6 for more details) :

{
    "presets": [[
        "@babel/preset-env",
        {
            "targets": {
              "node": "current"
            }
          }
    ]]
}

Save the above file under .babelrc

And now the jet.config.json file :

{
  "collectCoverage": true,
  "moduleFileExtensions": [
    "js",
    "mjs"
  ],
  "transform": {
    "^.+\\.js$": "babel-jest",
    "^.+\\.mjs$": "babel-jest"
  },

  "testRegex": "((\\.|/*.)(spec))\\.js?$"
}

Finally the package.json so you just have to run a npm install before running your script :

{
  "name": "fetch",
  "version": "1.0.0",
  "description": "Using Fetch in a POO environment - using Jest for testing",
  "main": "main.mjs",
  "scripts": {
    "test": "jest"
  },
  "author": "Eddy Mio",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/node": "^7.10.5",
    "@babel/preset-env": "^7.11.5",
    "jest": "^26.4.2",
    "jest-fetch-mock": "^3.0.3",
    "jest-mock-console": "^1.0.1",
    "regenerator-runtime": "^0.13.7"
  }
}

Time for a demo !

Issue the following commands :

$ npm install

$ node main.mjs

Node fetch api ES6 example
Result of NetUtils class implementation

You should get the same kind of result, the ExperimentalWarning log indicates that the current version of Node features an experimental use of ESM module loader. We already talked about it, browsers are compatible but the Node runtime environment can handle modules in different ways.

Using Jest and mocks

Following the documentation about Jest, we create a __mocks__ folder next to our module and main class.

Because we need to test our NetUtils object methods and the class is using the fetch-api, I’ve decided to use the Dependency Injection principle to avoid inner dependencies and ease the mocking strategy.

Let’s see the code for more details :

import { NetUtils } from '../NetUtils.mjs'
import fetchMock from 'jest-fetch-mock'

describe ('Proceed function', () => {
  beforeEach (() => {
    fetchMock.enableMocks()
    fetchMock.resetMocks()
  })

  test ('it should call fetch and action function without any error', async () => {
    fetchMock.mockResponseOnce(JSON.stringify({ body: 'hello' }))

    const mockFn = jest.fn().mockImplementationOnce()

    const utils = new NetUtils('http://localhost/', 'GET', {}, mockFn, fetchMock)
    await utils.proceed()

    expect(fetchMock).toHaveBeenCalledTimes(1)
    expect(mockFn).toBeCalledTimes(1)
  })
})

Store the above inside the folder under NetUtils.spec.js

Even if you are not familiar with Jest, you should get a fair understanding of what we actually test here !

There are 2 checks here :

  1. Basically we create a fake action (mockFn) which should be called upon reception of the fetch-api call – that function should be called 1 time
  2. We mock the fetch api and inject it within our object on creation so we master its use and can verify whether it has been called or not.

We could have been a step further with a check on the response but I let it for you as a training exercise ;).

Additional resources

There are actually quite a few things to clarify here but I rather invite you to follow the resources I’ve picked up for you.

If you have any questions or concerns, please let me know, I’ll be pleased to answer !

Using Fetch

The service Worker API

Promises and Using Promises

Async functions

Node fetch

About modules :

Modules features

Guide about modules

JS Script mode

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Want more information?

Related links will be displayed within articles for you to pick up another good spot to get more details about software development, deployment & monitoring.

Stay tuned by following us on Youtube.

%d bloggers like this: