nodemailer-mock
v2.0.10
Published
Easy as pie nodemailer mock for unit testing your Node.js applications.
Maintainers
Readme
nodemailer-mock
Easy as pie nodemailer mock for unit testing your Node.js applications.
install
npm install nodemailer-mock --save-devyarn add -D nodemailer-mockmodule loading
Depending on your mock configuration nodemailer-mock may, or may not, have access to nodemailer when it is loaded. For example, using mockery you can replace nodemailer with require('nodemailer-mock'), however in jest you will need to inject nodemailer using module.exports = require('nodemailer-mock').getMockFor(require('nodemailer'));
mock api
Use with test suites like jest, mocha, vitest, and node:test. There are some special methods available on the mocked module to help with testing. They are under the .mock key of the mocked nodemailer.
NodemailerMock.mock functions
reset: () => void- resets the mock class to default values
getSentMail: () => Mail.Options[]- returns an array of sent emails during your tests, since the last reset
getCloseCallCount: () => number- get the number of times the
transporter.close()was called across all instances
- get the number of times the
setShouldFailOnce: (isSet?: boolean) => void- should the mock return an error on the next call to
transporter.sendMail()ortransport.send()
- should the mock return an error on the next call to
isShouldFailOnce: () => boolean- returns status of
setShouldFailOnce(?)
- returns status of
setShouldFail: (isFail?: boolean) => void- indicate if errors should be returned for subsequent calls to
transporter.sendMail()ortransport.send()- if
true, return error - if
false, return success
- if
- indicate if errors should be returned for subsequent calls to
isShouldFail: () => boolean- returns status of
setShouldFail()
- returns status of
setShouldFailCheck: (check: CheckMailMessageOrNull) => void- indicate if the specific email passed to the function should fail the call to
transporter.sendMail()ortransport.send()- if function returns
true, return error - if function returns
false, return success
- if function returns
- use
type CheckMailMessageOrNull = ((email: MailMessage) => boolean) | null
- indicate if the specific email passed to the function should fail the call to
getShouldFailCheck: () => CheckMailMessageOrNull- returns the function used to check the
MailMessageornullif it is not set
- returns the function used to check the
setMockedVerify: (isMocked: boolean) => void- determine if a call to
transport.verify()should be mocked or passed through tonodemailer, defaults totrue.- if
true, use a mocked callback - if
false, pass through to a realnodemailertransport
- if
- determine if a call to
isMockedVerify: () => boolean- returns status of
setMockedVerify(?)
- returns status of
setMockedClose: (isMocked: boolean) => void- determine if calls to
transporter.close()should be passed through to the underlying transport, defaults totrue.
- determine if calls to
isMockedClose: () => boolean- when the result is
truethe underlying transport is not used, whenfalsethe call is passed through.
- when the result is
setSuccessResponse: (response: string) => void- set the success message that is returned in the callback for
transporter.sendMail()ortransport.send()
- set the success message that is returned in the callback for
getSuccessResponse: () => string- returns the success message value
setFailResponse: (error: Error) => void- set the
Errorthat is returned in the callback fortransporter.sendMail()ortransport.send()
- set the
getFailResponse: () => Error- returns the fail
Errorvalue
- returns the fail
scheduleIsIdle: (isIdle: boolean, timeout: number) => void- schedule a status change for calls to
transporter.isIdle()instances
- schedule a status change for calls to
setIsIdle: (isIdle: boolean) => void- set the status that is returned by calls to all
transporter.isIdle()instances
- set the status that is returned by calls to all
setUnmockedUsePlugins: (isUnmockUsePlugins: boolean) => voiddefaultfalse- should the plugins added via
transporter.use()be run outside the mock?
- should the plugins added via
isUnmockedUsePlugins: () => boolean- returns the status of
setUnmockedUsePlugins(?)
- returns the status of
NodemailerMockTransporter.mock functions
getPlugins: () => { [key: string]: Mail.PluginFunction<Mail.Options>[] }- returns the plugins that have been added via
transporter.use()as arrays, keyed by step
- returns the plugins that have been added via
getCloseCallCount: () => number- get the number of times
close()has been called on this transporter. this number is not reset with the mock.
- get the number of times
setIdle(isIdle: boolean): void- sets the idle state of
transporter.isIdle()and emits anidleevent when theisIdleargument istrue.
- sets the idle state of
usage
The mocked module behaves in a similar fashion to other transports provided by nodemailer.
setup test
const nodemailermock = require('nodemailer-mock');
const transport = nodemailermock.createTransport();
// the email you want to send
const email = ... // <-- your email hereuse nodestyle callbacks
// send with nodestyle callback
transport.sendMail(email, function(err, info) {
if (err) {
return console.log('Error!', err, info);
}
return console.log('Success!', info);
}
// verify with nodestyle callback
transport.verify(function(err, success) {
if (err) {
return console.log('Error!', err);
}
return console.log('Success!', success);
});use promises
// send with promises
transport.sendMail(email)
.then(function(info) {
console.log('Success!', info);
})
.catch(function(err) {
console.log('Error!', err);
});
// verify with promises
transport.verify()
.then(function(success) {
console.log('Success!', success);
});
.catch(function(err) {
console.log('Error!', err);
});use async/await
// send an email with async / wait
try {
const info = await transport.sendMail(email);
} catch (err) {
console.log("Error!", err);
}
// verify with async / wait
try {
const info = await transport.verify();
} catch (err) {
console.log("Error!", err);
}example tests using mocked module
To use nodemailer-mock in your tests you will need to mock nodemailer with it. There are working examples using jest, mocha, vitest, and node:test in the ./examples/ folder of the project. The jest code is in ./examples/__mocks__ and ./examples/__tests__, and the mocha, vitest, and node:test tests are in ./examples/test. Run the examples with npm run example:jest, npm run example:mocha, npm run example:node-test, and npm run example:vitest. Both JavaScript and TypeScript example tests are provided.
example using jest
To mock nodemailer using jest create a file called ./__mocks__/nodemailer.js that exports the mocked module:
/**
* Jest Mock
* ./__mocks__/nodemailer.js
**/
// load the real nodemailer
const nodemailer = require("nodemailer");
// pass it in when creating the mock using getMockFor()
const nodemailermock = require("nodemailer-mock").getMockFor(nodemailer);
// export the mocked module
module.exports = nodemailermock;Once the mock file is created all calls to nodemailer from your tests will return the mocked module. To access to mock functions, just load it in your test file.
/**
* Jest Test
* ./__tests__/my-test.js
**/
const { mock } = require("nodemailer");
test("Send an email using the mocked nodemailer", async () => {
/* ... run your tests that send emails here */
// check the mock for our sent emails
const sentEmails = mock.getSentMail();
// there should be one
expect(sentEmails.length).toBe(1);
// and it should match the to address
expect(sentEmails[0].to).toBe("[email protected]");
});Using typescript you can coerce the NodemailerMock type.
/**
* Jest Test
* ./__tests__/my-test.js
**/
import { expect, test } from "@jest/globals";
// 'nodemailer' is automatically mocked in ./__mocks__/nodemailer.js
import * as nodemailer from "nodemailer";
import { NodemailerMock } from "nodemailer-mock";
const { mock } = nodemailer as unknown as NodemailerMock;
test("Send an email using the mocked nodemailer + typescript", async () => {
/* ... run your tests that send emails here */
// check the mock for our sent emails
const sentEmails = mock.getSentMail();
// there should be one
expect(sentEmails.length).toBe(1);
// and it should match the to address
expect(sentEmails[0].to).toBe("[email protected]");
});example using mocha and mockery
Here is an example of using a mocked nodemailer class in a mocha test using mockery. Make sure that any modules that require()'s a mocked module must be called AFTER the module is mocked or node will use the unmocked version from the module cache. Note that this example uses async/await. See the module tests for additional example code.
/**
* Mocha Test / Mockery Mock
* ./test/my-test.js
**/
const { expect } = require('chai');
const mockery = require('mockery');
const nodemailermock = require('nodemailer-mock');
describe('Tests that send email', async () {
/* This could be an app, Express, etc. It should be
instantiated *after* nodemailer is mocked. */
let app = null;
before(async () {
// Enable mockery to mock objects
mockery.enable({
warnOnUnregistered: false,
});
/* Once mocked, any code that calls require('nodemailer')
will get our nodemailermock */
mockery.registerMock('nodemailer', nodemailermock)
/*
##################
### IMPORTANT! ###
##################
*/
/* Make sure anything that uses nodemailer is loaded here,
after it is mocked just above... */
const moduleThatRequiresNodemailer = require('module-that-requires-nodemailer');
});
afterEach(async () {
// Reset the mock back to the defaults after each test
nodemailermock.mock.reset();
});
after(async () {
// Remove our mocked nodemailer and disable mockery
mockery.deregisterAll();
mockery.disable();
});
it('should send an email using nodemailer-mock', async () {
// call a service that uses nodemailer
const response = ... // <-- your email code here
// a fake test for something on our response
expect(response.value).to.equal('value');
// get the array of emails we sent
const sentMail = nodemailermock.mock.getSentMail();
// we should have sent one email
expect(sentMail.length).to.equal(1);
// check the email for something
expect(sentMail[0].property).to.equal('foobar');
});
it('should fail to send an email using nodemailer-mock', async () {
// tell the mock class to return an error
const err = new Error('My custom error');
nodemailermock.mock.setShouldFailOnce();
nodemailermock.mock.setFailResponse(err);
// call a service that uses nodemailer
var response = ... // <-- your code here
// a fake test for something on our response
expect(response.error).to.equal(err);
});
/* this will not work with jest as all nodemailers are mocked */
it('should verify using the real nodemailer transport', async () {
// tell the mock class to pass verify requests to nodemailer
nodemailermock.mock.setMockedVerify(false);
// call a service that uses nodemailer
var response = ... // <-- your code here
/* calls to transport.verify() will be passed through,
transporter.send() is still mocked */
});
});example using vitest
To mock nodemailer using vitest, use vi.mock() to replace nodemailer with nodemailer-mock:
/**
* Vitest Test
* ./test/my-test.test.mjs
**/
import { describe, it, beforeEach, expect, vi } from "vitest";
import nodemailermock from "nodemailer-mock";
// Mock nodemailer with nodemailer-mock
vi.mock("nodemailer", () => {
return import("nodemailer-mock");
});
describe("Tests that send email", () => {
beforeEach(() => {
nodemailermock.mock.reset();
});
it("should send an email using nodemailer-mock", async () => {
// import the module under test after mocking
const nodemailer = await import("nodemailer");
const transport = nodemailer.createTransport({});
await transport.sendMail({ to: "[email protected]", from: "[email protected]" });
// check the mock for our sent emails
const sentEmails = nodemailermock.mock.getSentMail();
// there should be one
expect(sentEmails.length).toBe(1);
// and it should match the to address
expect(sentEmails[0].to).toBe("[email protected]");
});
});example using node:test
Node.js has a built-in test runner (node:test) with module mocking support. This requires Node.js >= v22.3.0 and the --experimental-test-module-mocks flag.
/**
* node:test Test
* ./test/my-test.mjs
*
* Run with: node --experimental-test-module-mocks --test ./test/my-test.mjs
**/
import { describe, it, beforeEach, mock } from "node:test";
import assert from "node:assert/strict";
// Register the mock *before* importing modules that use nodemailer
const nodemailermock = await import("nodemailer-mock");
mock.module("nodemailer", { namedExports: nodemailermock });
// Now import the module under test
const myModule = await import("./my-module.js");
describe("Tests that send email", () => {
beforeEach(() => {
nodemailermock.mock.reset();
});
it("should send an email using nodemailer-mock", async () => {
await myModule.sendEmail();
// check the mock for our sent emails
const sentEmails = nodemailermock.mock.getSentMail();
// there should be one
assert.equal(sentEmails.length, 1);
// and it should match the to address
assert.equal(sentEmails[0].to, "[email protected]");
});
});