1
votes

Just started using jest and the documentation doesn't seem to be too clear on mocking.

I have a module with the below code. If I want to test the send mail function, but not send an actual email out via mailgun what do I need to do here? I THINK in my test file I need to use the mock behaviour but can't work out how to specifically stop the mail going out, whilst also being able to check the right routes are followed e.g. invalid email address, error thrown, etc. Do I need to break this function down further?

const Mailgun = require('machinepack-mailgun');
const Emailaddresses = require('machinepack-emailaddresses');
const config = require('../Config');


// Function to send an email
const sendEmail = function (toaddress, toname, sub, msg, cb) {
// Validate the email address is correct and if so send an email. Otherwise log the error and exit.
  Emailaddresses.validate({
    string: toaddress,
  }).exec({
    error(err) {
      return cb(err, null);
    },
    invalid() {
      return cb(new Error('Email address is not valid.'), null);
    },
    success() {
      Mailgun.sendPlaintextEmail({
        apiKey: config.sender.apiKey,
        domain: config.sender.domain,
        toEmail: toaddress,
        toName: toname,
        subject: sub,
        message: msg,
        fromEmail: config.sender.fromEmail,
        fromName: config.sender.fromName,
      }).exec({
        error(err) {
          return cb(err, null);
        },
        success() {
          return cb(null, 'Email Sent.');
        },
      });
    },
  });
};

module.exports.sendEmail = sendEmail;
1

1 Answers

4
votes

You can mock out Mailgun with your own implementation which:

const Mailgun = require('machinepack-mailgun');
jest.mock('machinepack-mailgun', () = > ({
  sendPlaintextEmail: jest.fn()
}))
it('sends mail and fail', () = > {
  // no add the way `sendPlaintextEmail` should react.
  // in this case return `exec` which always run the `error` part
  Mailgun.sendPlaintextEmail.mockImplementation(() = > ({
    exec: (arg) = > {
      arg.error('someError')
    }
  }))
  const cb = jest.fn()
  sendEmail ('toaddress', 'toname', 'sub', 'msg', cb) 
  expect(Mailgun.sendPlaintextEmail).toHaveBeenCalledWith(...)
  expect(cb).toHaveBeenCalledWith(...)
})

In the above example we mock the mailgun module so that sendPlaintextEmail is a spy. Then we import the module into our test so we can set the mock implementation of the spy in every test. In the example we set the behaviour, so that it returns the exec method, which then is called by your code with the error/success object. The mock then just call the error part. After that you can first test that Mailgun.sendPlaintextEmail was called with the correct parameters, then you can test that cb was called with "someError" and null.

In another test you can just set the behaviour of exec so it will call the success method.