Mocking SendMailer in NodeJs

I am not an expert in NodeJs. I have started working with NodeJs from few weeks :)
My first experience with NodeJs was awesome. It's simple and yet powerful framework. Visit NodeJs official site for more details.

User Case
I was working on a simple feature which does two activities.
  1. Create User record in database
  2. Send welcome E-mail to user.
I had created all the other prerequisites like having User model, configuring Email service etc..
In this case, I am using NodeMailer module and had configured the required prerequisites to use NodeMailer module.

REST endpoint consumes User JSON object to create User record in database and then sends an welcome email.

Let's talk about the problem:

As a good programmer with good programming practices, I started following TDD with NodeJs. Starting using Mocha and Should modules as my unit testing frameworks.

It was simple enough to add tests in Mocha that covered the first use case. Since, all my tests were passing, I wanted to introduce the logic of sending email as well. As soon as I introduced email functionality, all my tests started failing. Intent of the tests is not to validate the email module. Instead just wanted to verify the email body, to address, etc.. Then what is the best solution?

Mocking:
Since, I was writing tests for my route file,  I wanted to mock the email service call.

How to mock NodeMailer's send() function?
Let's assume that you are creating the NodeMailer instance as below in your production code:

Production code snippet:
var smtpTransport = nodemailer.createTransport(config.mailer.options);    
var mailOptions = {
       to: user.email,
        from: config.mailer.from,
        subject: 'Account Created',
        html: emailHTML
      };
      
      smtpTransport.sendMail(mailOptions, function (err) {
        if (!err) {
          res.send({
            message: 'An email has been sent to the provided email with further instructions.'
          });
        } else { 
          console.log(err);
          return res.status(400).send({
            message: 'Failure sending email'
          });
        }

        done(err);
      });
Notice, that the createTransport() method takes a parameter that sets the default mailing options for NodeMailer. 

Mocking in Unit test file:
var should = require('should'),
  request = require('supertest'),
  path = require('path'),
  config = require(path.resolve('./config/config'));
  
  // Mocking Send method for test
  config.mailer.options.send = function(data, callback) {
    // Add assertions if required.
    callback();
  };

How does this work?
NodeMailer's sendMail() function first check if send() function is defined in the configuration. If yes, then it invokes the send() function or else it invokes the default send() function of NodeMailer.
In the test class, I am intercepting send() method at the begining. Hence, instead of invoking the default send() method, it invokes the send() function defined in test class.

This is one of the way to mock the methods in NodeJs. This technique also holds good for SendGrid or any other email modules in NodeJs.
I am not saying the this is be best code written. There are areas to refactor this. In my next post, I will discuss the refactoring technique's to make the code easy to test.

Hope, this helps others. Feel free to add your comments and suggestions. 

No comments:

Post a Comment