I've got this emailService module in my project:
import config from "../config";
import formData from "form-data";
import Mailgun from "mailgun.js";
import MailOptions from "../@types/MailOptions";
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
username: "api",
key: config.mailApiKey,
url: config.mailApiUrl,
});
export const sendMail = (mailOptions: MailOptions): Promise<unknown> => {
return new Promise((resolve, reject) => {
mg.messages
.create(config.mailDomain, mailOptions)
.then((msg) => resolve(msg))
.catch((err) => reject(err));
});
};
export const setMailOptions = (
recipientEmail: string,
subject: string,
body: string
): MailOptions => {
const mailOptions: MailOptions = {
from: config.senderEmail,
to: recipientEmail,
subject: subject,
html: body,
};
return mailOptions;
};
Normally I import this emailService.ts in any other module I want to send emails in.
I'm trying to figure out how to test the sendMail() in emailService, and assert that mg.messages.create() was called.
I have a feeling I need to refactor this code so that I can mock the config values and Mailgun instance being used by emailService, unless there's an alternative way to mock Mailgun in the test suite in such a way that that the emailService would use it?
You'll need to export your mailgun client instance in order to mock it.
mailgun.ts
:
import config from "../config";
import formData from "form-data";
import Mailgun from "mailgun.js";
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
username: "api",
key: config.mailApiKey,
url: config.mailApiUrl,
});
export default mg;
mailgunMock.ts
:
import { mockDeep, DeepMockProxy } from 'jest-mock-extended';
import Client from 'mailgun.js/client';
import mg from '../mailgun';
jest.mock('../mailgun', () => ({
__esModule: true,
default: mockDeep<Client>(),
}));
export const mailgunMock = mg as unknown as DeepMockProxy<Client>;
You can then use this mock in your test suite:
emailService.test.ts
:
import { mailgunMock } from './mocks/mailgunMock';
import { sendMail } from '../emailService';
describe('send email', () => {
const emailMessage = {
id: 'test',
message: 'test',
status: 200,
details: 'n/a',
};
test('setup mocks', async () => {
mailgunMock.messages.create.mockResolvedValue(emailMessage);
});
test('should send email', async () => {
await expect(
sendMail({from: 'test', to: 'test', subject: 'test', html: 'test'}),
).resolves.toEqual(emailMessage);
expect(mailgunMock.messages.create.mock.calls[0][1]).toEqual(
expect.objectContaining({
from: 'test',
to: 'test',
subject: 'test',
html: 'test',
}),
);
});
});