Logo

dev-resources.site

for different kinds of informations.

Partial: how NOT to mock the whole world

Published at
2/8/2024
Categories
javascript
mocking
testing
Author
thekashey
Categories
3 categories in total
javascript
open
mocking
open
testing
open
Author
9 person written this
thekashey
open
Partial: how NOT to mock the whole world

Mocking is a very controversial concept in testing - some thought leaders advocate it as something "bad"😤, while others have to use mocks🙃 one way or another.
No matter what we think about it - mocks are around.

However, mocks can be different

  • they could be network mocks (use msw)
  • or they could be state mock injected by jest.mock or any other force (I would recommend magnetic-di)

But in all cases, there is a hill to die on - are you going to mock the entire universe 🛸 or just a little bit?

Let's start with the 🪐 entire universe

Jest.mock

jest.mock(or vi.mock) is a good example of "everything" - this helper mocks the entire module providing "empty stubs" for the original content.

Imagine a use case



// index.ts
import {getConfiguration} from './config';

export const isAdmin = () => getConfiguration().user.isAdmin;


Enter fullscreen mode Exit fullscreen mode


// index.spec.ts
import {getConfiguration} from './config';
import {isAdmin} from './';

jest.mock('./config');

test('isAdmin', () => {
  // ??? 🤷‍♂️
  expect(isAdmin()).toBe(true); 
});


Enter fullscreen mode Exit fullscreen mode

To make this test run you need to specify the correct mock override for getConfiguration. Can you do it?

Let's try



  jest.mocked(getConfiguration).mockReturnValue({
    // 50 different fields
    user:{
     // 20 other fields,
     isAdmin: true
    }
  });


Enter fullscreen mode Exit fullscreen mode

So you can, but you have to specify the "whole world", or typescript will not be happy.

You will not like it as well 🤮

What about defaults?

A better way to manage mocks is by providing defaults one can "extend" from

For example



import {defaultConfiguration} from './configuration-test-utils';
// ...
jest.mocked(getConfiguration).mockReturnValue(
    merge({}, defaultConfiguration, {
       user:{
        isAdmin: true
      }
);


Enter fullscreen mode Exit fullscreen mode

That would greatly simplify life and establish an environment easier to maintain.

However it is still about mocking the whole world, while you need only one field 😭

Only one!

In out case we dont need to mock everything, it's more about TypeScript being too type-safe. Let's stop this!



jest.mocked(getConfiguration).mockReturnValue({
    user:{
     isAdmin: true
    }
} as any/* 😘 */);

// test passes ✅


Enter fullscreen mode Exit fullscreen mode

Well, as any is not the best idea. Here is where DeepPartial helps

Partial is a TypeScript helper making keys of an object non-required. DeepPartial is just a recursive helper for it. It has many implementations, here is one

This gives us the ability to write code like



jest.mocked(getConfiguration).mockReturnValue({
    user:{
     isAdmin: true
    }
} as DeepPartial<ReturnType<typeof getConfiguration>>);



Enter fullscreen mode Exit fullscreen mode

Yeah, the last line is 🤮 and one can improve it with utilities like shoehorn hiding all complexity underneath.



import { fromPartial } from "@total-typescript/shoehorn";
jest.mocked(getConfiguration).mockReturnValue(fromPartial({
    user:{
     isAdmin: true
    }
}));


Enter fullscreen mode Exit fullscreen mode

Better? Better!

....

However, what would happen if we change our code? You know - code always drifting somewhere...



export const isAdmin = () => (
  getConfiguration().user.isAdmin || 
  getConfiguration().user.isSuperAdmin
)


Enter fullscreen mode Exit fullscreen mode

No tests will notice the difference, but our partial mock is no longer the correct one for our use case.

We need something better.

“actually better” is to refactor the code in a way you dont need to mock everything, but we are trying to complete the task without changing the game rules as not everybody can afford refactoring and not everybody want to make their test more testable for the sake or abstract testability (aka test induced design damage)

Something better

Let's change our test a little bit



// index.spec.ts
import {partialMock} from 'partial-mock'; // ⬅️⬅️
import {getConfiguration} from './config';
import {isAdmin} from './';

jest.mock('./config');

test('isAdmin', () => {
  // ⬇️⬇️
  jest.mocked(getConfiguration).mockReturnValue(partialMock({
    user:{
     isAdmin: true
    }
  });

  expect(isAdmin()).toBe(true);
  // but instead it will throw 
});


Enter fullscreen mode Exit fullscreen mode

Here we used partialMock utility to apply DeepPartial(but shoehorn can do it), but also break test because the provided mock no longer represents the case.

Failure example

What about "over mocking"?

Imagine the opposite situation - something complex becomes simpler, but your mocks are still too much.

For example, imagine we do define isSuperAdmin missing in the example above, but we will no longer use it



const mock = partialMock<Data>({
  isAdmin: true,
  isSuperAdmin: true,
});
expectNoUnusedKeys(mock);


Enter fullscreen mode Exit fullscreen mode

Sandbox - https://codesandbox.io/p/sandbox/overmocking-partial-mock-example-z8qd5w?file=%2Fsrc%2Findex.ts%3A22%2C1

unused keys

That's it, folks

That's it - partial mock helps with the "over-mocking", situations where you mock too much, and it also solves problems with the "under-mocking" where you missing some important pieces.

Pretty sure you never though about under-mocking. Until now.

Link to follow:

mocking Article's
30 articles in total
Favicon
4. Testing (async) searchParams with Jest in Next 15
Favicon
Enhancing API Workflows: A Comprehensive Guide to Mock Responses and Efficient Testing
Favicon
Dummy API for Testing: How to Use It vs Mocking
Favicon
How Better Mocking Makes Building APIs Smoother and Speed Up Development
Favicon
A Step-by-Step Guide to API Creation and Mocking for Faster Development
Favicon
How I've been trying to improve mocking with Zod
Favicon
How to Set Up a Mock Server
Favicon
Best API Mocking Platforms in 2024
Favicon
Server Side Mocking for Playwright in NextJS (App Router) using Mock Service Worker
Favicon
How Mocking Against a Public API Endpoints within Blackbird Gets your API in Production Faster
Favicon
10 Best API Mocking Tools (2025 Review)
Favicon
Introduction to Jest: Unit Testing, Mocking, and Asynchronous Code
Favicon
Mocking an AI Chatbot API with Blackbird
Favicon
Incredibly Useful WireMock with IntelliJ IDEA
Favicon
Simplifying Mocking APIs for Faster Development: Basic & Advanced Techniques
Favicon
The Case Against Mocking Libraries
Favicon
Mocking an AI Chatbot API with Blackbird
Favicon
API Mocking: A Comprehensive Guide
Favicon
Mock TypeORM Package
Favicon
Level Up Your Node.js Testing with Native Test Runner and Mocks: A BigQuery Example
Favicon
Unit Tests for Frontend Developers [Part 2]
Favicon
Revamped Mock-API.net: Simplifying API Mocking for Developers
Favicon
Mocking with Sinon.js: A Comprehensive Guide
Favicon
Streamline Development with Effective API Mocking
Favicon
API mocking with Mock Service Worker + Vue.js
Favicon
Partial: how NOT to mock the whole world
Favicon
Introducing Mockallan: Testing with API-Level Stubs and Mocks
Favicon
Como fazer Deploy de uma api com Json-Server
Favicon
What's the real ROI of writing Mocks?
Favicon
Unleashing GraphQL Type-Based Mocking

Featured ones: