6

Mastering Unit Testing: How to Mock Dependencies with Jest

 1 year ago
source link: https://blog.bitsrc.io/mock-dependencies-using-jest-274c31ec9cd0
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Mastering Unit Testing: How to Mock Dependencies with Jest

0*5oUsrW7sGh88FiVv

Photo by Sigmund on Unsplash

I started to practice test-driven programming a couple of years back. Depending on each project's settings, the strictness of the unit test can vary. For instance, one project requires to have a coverage coupled so if the percentage isn’t met, the code can’t be committed. Therefore I have to refresh all the possible ways to mock things under Jest.

Baseline of the mocking

Let’s start with easy ones with a function that is relatively easy to test:

function app = (fn) => {
const a = fn()
...
return ...
}

In order to test app we can make a mock replacement for fn and then pass it into your app as usual:

test('app', () => {
let mockedFn = jest.fn()
app(mockedFn)

expect(mockedFn).toHaveBeenCalled()
})

Not only the above approach allows our function to be tested, but also it provides us an extreme level of confidence. As long as we can provide a fn we can control the robustness of this unit, even when it’s a black box with no access to the implementation. Notice the reason for our confidence level isn’t based on luck, instead, it’s that the dependencies are all declared on the interface. Exactly for this reason, when designing your own function, you should aim for this goal because it’ll make your life a lot easier.

When testing gets difficult

Unfortunately, we can’t always ask for the perfect interface design. Half of the time, unless writing a library from scratch, you will run into a case where the written function doesn’t carry that implicit interface. Let’s take a look at one example:

import axios from 'axios'

function app() {
const user = axios.get('qplot.com');
if (!user) return null

// now we can use this user
return ...
}

We want to test a function where inside we fetch the user over the internet and then we move to the rest of the code based on this user object. Naive or small as the function looks, it’s kind of very difficult to write a test. Believe it or not, this is the number one place where a junior developer quits the test job that he is supposed to perform. Let’stake a second to understand why it’s that.

The function app doesn’t have input parameters, aka explicit interface, however, it has a hidden dependency axios which isn’t written but referenced inside. Reflecting on what we have stated at the beginning unless we can control all dependencies precisely, we won’t gain the confidence that we need. Therefore, the solution is to find a way to mock axios so that we can control how it behaves in the testing environment. Let me show you one quick example of mocking it:

import axois from 'axois' 

jest.mock('axios', () => ({
get: (url) => Promise.resolve('fang')
}))

test('app', () => {
app()
// do rest of testing
})

We first import the axios and acknowledge there’s a get function that we want to mock the response. Therefore whenever inside the app when axios.get is called, it’ll run our mocked version, in this particular test we want to set user equals to fang without going a round trip to the internet.

Ok, let’s retrospect a bit, in order to test a unit, we should find out all apparent and hidden dependencies, and make sure you can mock all of them to recover the perfect crime scene. After that based on these capabilities, then you can come up with any number of tests to back your functionalities.

People might argue here, mocking axios seems like dodging the testing responsibility. This is where the unit testing and rest of the testings start to branch out. Here we don’t really care about axios , in a way, even when axios is broken, we’d like to finish our test. This is actually the definition of a unit test in a verbose form.

You might wonder why we ever want to have hidden dependencies. This is a more philosophical question: if you live in a world, you can’t live by yourself, no matter how hard you try, you will end up with some relationship that is not apparent to the surface. Writing a function with all sorts of dependencies operates in a similar fashion. Technically, you can call the source of these dependencies from global variables, external libraries, or side effects. However you understand them, they are in general not under our control.

💡 Dependency-management becomes infinitely more streamlined if you use Bit. Within a Bit Workspace, you don’t need to tell npm, pnpm, or yarn which component dependencies need to be installed, nor if it’s a dev or prod dependency. Bit dynamically generates package.json files for each, and can handle this for you painlessly. Find out more about this hereand here.

Learn more here:

Summary

I showed you ways to mock both apparent and hidden dependencies. So when you write tests, find them first, and then mock them, happy coding ;)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK