skip to Main Content

I’m honestly confused about the goal of mocking certain functions when unit testing my app. I’ve seen articles suggesting I should mock everything I test, but also others suggesting I should avoid mocking as much as possible.

I need some help understanding the decision behind to mock or not parts of my code.

Let’s say I have the following function:

export const numberFormat = (num) => {
  return new Intl.NumberFormat('en-EN', {
    style: 'decimal',
    maximumFractionDigits: 2
  }).format(num) 
}

And I decided to test it by importing the literal function, like that:

import { test, expect } from 'vitest'
import { numberFormat } from './numberFormat.js'

const numValue = 1000.12345678

test('number is formatted', () => {
  expect(numberFormat(numValue)).toBe('1,000.12')
})

Would this be wrong? I’m quite literally just formatting a number, why should I mock this function?

2

Answers


  1. When we are to plan or define unit tests, then we want to separate the things we are to test into atomic and very simple units. If you have an f function, then these are the parts you may wonder about regarding it:

    • the algorithm of f
    • the separate functions f is calling
    • the ways f may be used alike

    If you wonder about the algorithm of f, then your units would mock the functions f is calling with the idea that okay, let’s assume that those functions were executed and their result was this and this and this. Would f perform well in that scenario? So in this case you mock the functions f is calling but test f.

    If, on the other hand you are interested about the functions f is calling, then you need to differentiate between the use-cases of such functions and test them without mocking them.

    Finally, if you are interested about f‘s usages and not f itself, then you mock f and test its use, with the idea of f behaving in certain manners and how its usage would respond to this situation.

    So, whenever you take f into account, the question is what aspect of f are you interested about in that specific test. Is it some function f is calling? Or f itself? Or its usage? And in order to decide whether to mock or not to mock f you will need to answer these questions.

    Login or Signup to reply.
  2. As a general rule, there are three groups of components that need to be replaced in tests with test doubles:

    1. components that use resources that are not in the memory of the running process; i.e. databases, caches, files, HTTP requests, queues, etc;
    2. components that use randomness or the current date and time;
    3. components that, although are not included in the above categories, take a long time to execute; the definition of "long time" might vary but, for unit tests, anything that lasts more than 0.1 seconds is long.

    The reasons why such components are replaced with test doubles:

    1. speed – the components in the first group above tend to require long time to do their job; also, the third group deals specifically with this goal;
    2. consistency – the tests must produce the same outcome on each execution; this is difficult when the input data changes every time (the second group above);
    3. isolation – the tests can be executed anytime (second group), anywhere (first group); they do not depend on a setup that could be outside our control or that can be offline (first group).

    Replacing with test doubles a component that does not belong to any of the groups above is a smell. It usually signals a component that has too many dependencies (and hidden dependencies) and is difficult to create.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search