2

I'm trying to mock an object to test some implementation using jest which can be overridden for each testcase. Please let me know if you have solved similar problem or any other better solution to mock an object using jest.

import {obj} from "./filepath/obj";

jest.mock("./filepath/obj", () => ({
  obj:{
    search: jest.fn(),
    items:[1,2,3]
  }

}))

test("test 1", () => {
  expect(obj.items.length).toBe(3); // works
})

// now if I try to override, for some other test case that doesn't work
test("test 1", () => {
  jest.mock("./filepath/obj", () => ({
    obj:{
      search: jest.fn(),
      items:[]
    }

  }))
   
  expect(obj.items.length).toBe(0); // doesn't work
})

How can we override the mock implementation for an object which can work in each test case?

more context: the object is a mobx store object exported from the file where we have the store definition. Then, we're using that store object in a component by importing it. Now, while testing the component, we're trying to mock the store object as mentioned above.

sjsouvik
  • 113
  • 1
  • 8

1 Answers1

1

There is no need to pass the factory parameter to jest.mock(), just let jest mock a module with an auto-mocked version when it is required.

That means the obj.items will be replaced with empty array [], the original obj.search() method will be replaced with jest.fn(). You can change the value of obj.items property in each test case by assignment.

E.g.

obj.js:

const obj = {
    search: () => 'real implementation',
    items: [0, 0, 0],
};

export { obj };

index.test.js:

import { obj } from './obj';

jest.mock('./obj');

describe('76570418', () => {
    test('should pass 0', () => {
        expect(obj.items).toHaveLength(0);
        expect(jest.isMockFunction(obj.search)).toBeTrue();
    });
    test('should pass 1', () => {
        obj.items = [1, 2, 3];
        expect(obj.items).toHaveLength(3);
        expect(jest.isMockFunction(obj.search)).toBeTrue();
    });
    test('should pass 2', () => {
        obj.items = [];
        expect(obj.items).toHaveLength(0);
        expect(jest.isMockFunction(obj.search)).toBeTrue();
    });
    test('should pass 3', () => {
        Object.assign(obj, { items: [1], a: 1 });
        expect(obj.items).toHaveLength(1);
        expect(obj.a).toBe(1);
        expect(jest.isMockFunction(obj.search)).toBeTrue();
    });
});

Test result:

 PASS  stackoverflow/76570418/index.test.js (7.347 s)
  76570418
    ✓ should pass 0 (1 ms)
    ✓ should pass 1 (1 ms)
    ✓ should pass 2
    ✓ should pass 3 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        7.616 s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • This works. Thanks. Is there any better way to assign the property value of the mocked object instead of doing it for all properties one by one? ``` obj.a = 'adjdm'; obj.b = "hdjjdj"; ...... ``` – sjsouvik Jun 28 '23 at 08:42
  • @sjsouvik Spread operator or `Object.assign()`, keep it simple. see https://stackoverflow.com/questions/43436727/multiple-object-properties-assignment-in-javascript – Lin Du Jun 28 '23 at 08:50
  • But, it won't allow us to assign like this `obj = Object.assign({}, { a: 1, b: [] })` since `obj` is an import. – sjsouvik Jun 28 '23 at 09:17
  • 1
    @sjsouvik You can't reassign it. Just modify it, like `Object.assign(obj, { a: 1, b: [] })`. I have updated the answer. Also, take a look https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign – Lin Du Jun 28 '23 at 09:18
  • Thanks @Lin for the quick response, it was helpful. – sjsouvik Jun 28 '23 at 09:31