'How do I completely mock an imported service class
I am looking to test my next API route which uses the micro framework (similar enough to express when using next-connect).
I have a service:
export class UserService {
constructor(...) {}
async findUser({ email }: Pick<IUserModel, 'email'>) {
...
}
}
My API endpoint:
import { NextApiRequest, NextApiResponse } from 'next';
import nc from 'next-connect';
import { UserService } from './UserService'; // Mock and prevent execution
const userService = new UserService();
export default nc().post(async (req: NextApiRequest, res: NextApiResponse) => {
try {
userService.findUser({ email: '[email protected]' });
return res.status(200).send({ done: true });
} catch (error: any) {
return res.status(500).end(error.message);
}
});
I would like to completely mock the import and prevent any dependent code from executing.
import { UserService } from './UserService';
For example if there is a console.log() in the UserService constructor it should not be run because the import is a completely mocked import.
Update:
I've attempted to use jest.mock but they didn't seem to actually mock my imports. I've added a console.log in the UserService constructor and it continues to be triggered when using jest.mock.
import signup from './signup';
jest.mock('./UserService');
describe('signup', () => {
it('should complete the happy path', async () => {
const req: IncomingMessage = {} as unknown as IncomingMessage;
const res: ServerResponse = {
end: jest.fn(),
} as unknown as ServerResponse;
const actual = await signup(req, res);
expect(actual).toBe(...);
});
});
Solution 1:[1]
If you mean mocking in a jest test then you can just use jest.mock('./userService'); or jest.spyOn(UserService, 'findUser') if you want to mock one method.
If you want a mock for a specific use case, you would create a mock service and conditionally import based on some flag.
Ex:
// UserService.js
export class UserService {
constructor(...) {}
async findUser({ email }: Pick<IUserModel, 'email'>) {
// calls real API
}
}
// UserService.mock.js
export class MockUserService {
constructor(...) {}
async findUser({ email }: Pick<IUserModel, 'email'>) {
// calls fake API or just returns a promise or anything you want
}
}
// Your Endpoint
import { NextApiRequest, NextApiResponse } from 'next';
import nc from 'next-connect';
import { UserService } from './UserService';
import { MockUserService } from './UserService.mock'; // Mock
let userService;
if (someCondition) {
userService = new UserService();
} else {
userService = new MockUserService();
}
...
// The idea is you want to dynamically change what you're importing
const Something = process.env.NODE_ENV === 'development' ?
require('./FakeSomething') : require('./RealSomething');
Solution 2:[2]
In my case I have a lot of dependencies in my Next API endpoint that cannot be mocked.jest.mock doesn't support mocking dependencies for imports outside of the actual test file (.spec.ts & .test.ts).
You'll have to find a pattern to inject the dependency into the endpoint and test it in isolation instead. I made use of the Nextjs middleware example here but there are likely other patterns that will work just as well.
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
// Testable function for DI
export function post(
_userService: UserService,
) {
return async function(req: NextApiRequest, res: NextApiResponse) {
...
}
}
// Endpoint
export default async function(req: NextApiRequest, res: NextApiResponse) {
try {
await runMiddleware(req, res, post(userService));
return res.status(200).send({ done: true });
} catch (error: any) {
return res.status(500).end(error.message);
}
};
// Test usage
describe('signup', () => {
let userService: UserService;
beforeAll(() => {
userService = new UserService({} as unknown as UserRepository);
});
it('should complete the happy path', async () => {
const findSpy = jest
.spyOn(userService, 'find')
.mockImplementation(async () => null);
const req: NextApiRequest = {
body: {
email: '[email protected]',
password: '12345678',
},
} as unknown as NextApiRequest;
const res: NextApiResponse = {} as unknown as NextApiResponse;
await post(userService)(req, res);
...
});
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | SoluableNonagon |
| Solution 2 |
