jest-mock-extended
jest-mock-extended copied to clipboard
How to handle hoisted mocks
Let's take the following code snippet:
import { mockDeep } from "jest-mock-extended";
// ... other imports
jest.mock("./someObject", () => ({
someObject: mockDeep<InstanceType<SomeClass>>(),
}));
This would fail because jest hoists jest.mock
above all the import
s and so mockDeep
would be undefined.
How do I solve this problem? And would it be worthwhile adding documentation on how to do this?
import { PrismaClient } from '@prisma/client';
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';
const mockDeepFn = mockDeep; // set the import to a const that starts with 'mock' for Jest
import prisma from './prisma';
//
// Prisma setup for unit testing with Jest
//
// @see https://www.prisma.io/docs/guides/testing/unit-testing
//
jest.mock('./prisma', () => {
var mockPrismaClient = mockDeepFn<PrismaClient>();
return {
__esModule: true,
default: mockPrismaClient,
};
});
beforeEach(() => {
console.log(prismaMock);
mockReset(prismaMock);
});
export const prismaMock = prisma as unknown as DeepMockProxy<PrismaClient>;
I have a different error now, my prismaMock
is not defined, but it fixed the hoisting problem
@phil-lgr did you get past the issue? I'm at the same point that you are.
Probably worth adding to docs... but you can do this:
import { mockDeep } from "jest-mock-extended";
// ... other imports
jest.mock("./someObject", () => {
const nonHoistedMockDeep: typeof mockDeep = jest.requireActual("jest-mock-extended").mockDeep;
return {
someObject: nonHoistedMockDeep<SomeClass>(),
};
});
I was wondering you'd have to do a dynamic import in the jest.mock or if that's even possible, but I forgot all about jest.requireActual
.
@toshi38 this is absolutely worth adding to the docs, this is a scenario that we would encounter quite frequently.
Closing this issue as resolved, but yes definitely worth adding to the docs!
I'm back here again from a Google search 😆
I think another important part of this that I'm not sure has been handled or explained is how to get proper type safety when utilizing jest.mock
?
A large reason I enjoy this library is the proper typing it gives after mocking something. When using jest.mock
though, there doesn't seem to be a great way around this. Has anyone figure anything out for that?
Edit
It seems this may be the best route; create a reference and type it as such:
This is the scenario where Something
is an object literal, so I had to use typeof
in the casting, but otherwise I imagine this applies to other classes as well [but w/o the typeof
].
Dependency to be mocked
something.ts
export const Something = {
someMethod: (data: SomeType): YetAnotherType => {
// do stuff
return new YetAnotherType(data)
}
}
Consumer & Test
consumer.ts
import { Something } from 'something'
export class Consumer {
doSomething() {
return Something.someMethod(new SomeType())
}
}
consumer.test.ts
jest.mock('./something', () => {
const nonHoistedMockDeep: typeof mockDeep =
jest.requireActual('jest-mock-extended').mockDeep
return {
Something: nonHoistedMockDeep<typeof Something>(),
}
})
// This will give you proper type safe autocomplete & TS validation
const SomethingMock = <DeepMockProxy<typeof Something>>Something
beforeEach(() => {
SomethingMock.someMethod.mockReturnValue()
})
It almost makes me want to go back to dependency injecting everything which jest-mock-extended
works great for, but I feel like the language provides import
as a feature that shouldn't be avoided or fought. It just feels like a lot of footwork for something that should be trivial.