Automated testing is a great way to improve the quality and speed throughout the development of your code. In this article I'll explain some of the benefits of automated testing, show you how to add testing to an existing project, write tests, and then write code to make the tests pass. This article is written with the assumption you are familiar with Node.js and Gulp.

Benefits of Automated Testing

Adding automated tests to your code offers several benefits:

  • You understand the requirements better.
  • You know that you've correctly implemented the solution.
  • You know that all of the code you have written has been executed.
  • You can have great confidence when refactoring or adding features to your code that you haven't introduced any regressions.

Add Testing to Your Project

In order to get the most out of this process, you want to add tasks to your Gulp file so that the tests are re-run as you add code.

The testing framework that we'll use in this example is Jasmine.

  1. Install Jasmine: npm install --save-dev jasmine jasmine-core jasmine-spec-reporter gulp-jasmine

  2. Edit gulpfile.js and require the testing modules:

    var jasmine = require('gulp-jasmine');
    var SpecReporter = require('jasmine-spec-reporter');
  3. Add the testing task:

    gulp.task('test:js', function() {
    return gulp.src('test/**/*.spec.js')
        includeStackTrace: true,
        reporter: new SpecReporter()
  4. Add the following watcher task:

    gulp.task('test:watch-js', function () {['js/**/*.js', 'test/**/*.spec.js'],
  5. This assumes that all tests are under the test directory and all server-side JavaScript files are under the js directory.

    Writing Your First Test

    We'll be testing a file named lib/helpers/acccountVerification.js. It is used to send an SMS message to a user registering for our service. The complete spec file is located here: test/accountVerification.spec.js

  6. Create a file under the test directory named test/accountVerification.spec.js.

  7. In the file add a describe:

    var accountVerification = require('../lib/helpers/accountVerification'),
    twilio = require('twilio'),
    Q = require('q');
    describe('accountVerification', function () {
  8. We require the file we're going to be testing and the third-party NPM module (twilio-node) that our code uses. The Q module will be described later. The first parameter to describe matches the name of the item being tested. Now add an it clause with an expectation:

    it('should exist', function () {
  9. The description string passed into the it function should always start with "should." The descriptions strings for the describes and its are combined; so make sure that they make sense. In this case, "accountVerification should exist." The expect function takes as its sole argument the object being tested. There are many matchers. See the Jasmine Matchers.

  10. Now run gulp test:js. It runs the Jasmine specs and produces the following output:

    Spec started
      ✓ should exist
    Executed 1 of 1 specs SUCCESS in 0.005 sec.
  11. If you run gulp test:watch-js, the test will be re-run whenever the spec file is changed or a JavaScript file is changed.

    Adding More Tests

  12. Now add tests for the sendVerificationCode function:

    describe('sendVerificationCode', function () {
      var sendVerificationCode,
        phone = '3145551212',
        email = '';
      beforeEach(function () {
        sendVerificationCode = accountVerification.sendVerificationCode;
      it('should be a function', function () {

    Note how the combined description strings make sense: "accountVerification sendVerificationCode should be a function".

  13. We're using the beforeEach function. The function passed in will be executed before each it and describe function within the parent describe's function. The phone and email parameters will be used when we call sendVerificationCode.

    Testing a Function Call

    Let's call the sendVerificationCode function and test its execution. Now it returns a promise. So we will have to provide functions for both success and failure.

  14. We do this by creating "spy" functions. These functions record their arguments and how any times they're called.

      describe('when called', function () {
        var success, failure;
        beforeEach(function () {
          success = jasmine.createSpy('success');
          failure = jasmine.createSpy('failure');
          sendVerificationCode(phone, email)

    We're writing a "black box" test. That means we can only test by calling the module's public API and mocking and checking the external functions that the module calls. We notice that sendVerificationCode calls validatePhoneNumber which is a private function. Notice that validatePhoneNumber calls new twilio.LookupsClient, then lookups.phoneNumbers and then lookups.phoneNumbers().get which are all external functions. So we'll need to mock these. We do this using the Jasmine functions createSpy and spyOn. createSpy creates a function that does nothing and spyOn replaces an existing function with a spy that does nothing.

    However, we can specify that a spy will return a value whenever it is called using either and.returnValue or and.callFake. In this case, we mock the LookupsClient constructor to set the property phoneNumbers. phoneNumbers itself is a spy that returns an object with a get property, which is another spy function. Finally, the get function returns a promise. We create a deferred object using the "Q" library and we return the promise of that deferred. Later on we can call resolve or reject on this promise, depending on whether we want to test the success or failure condition.

  15. We'll mock the LookupsClient API in the first beforeEach function since these mocks will be used throughout our tests.

    beforeEach(function () {
      // Mock LookupsClient API
      phoneNumbersGetDefer = Q.defer();
      phoneNumbersGet = jasmine.createSpy('phoneNumbersGet')
      phoneNumbers = jasmine.createSpy('phoneNumbers')
        .and.returnValue({get: phoneNumbersGet});
      spyOn(twilio, 'LookupsClient').and.callFake(function () {
        this.phoneNumbers = phoneNumbers;
  16. We test that the LookupsClient constructor function is called. Then, we test that the phoneNumbers function is called with the phone number we will be sending the message to. To do this we use the toHaveBeenCalledWith matcher. Finally, we test that the get function is called with the option to get carrier information on the phone number.

        it('should call LookupsClient', function () {
        it('should call LookupsClient.phoneNumbers', function () {
        it('should call LookupsClient.phoneNumbers().get', function () {
            type: 'carrier'

    If you think this looks pretty involved, you are correct. Most of the time external libraries are called with a single function, not a chain of constructors and functions.

    Testing Promises

    Now we want to test when the promise that the get function returns is resolved or rejected. In order to do this we need to know what is returned by the API call in each case. One way to find this out is to add console.log to the code. Another way is to check out the API's documentation.

    To resolve the get promise, we call phoneNumbersGetDefer.resolve passing the mock phone information. It is important to always call process.nextTick. The then function on the promise is not called until the next tick after it is resolved. Also, there is the done function, which is an optional parameter to beforeEach. It is used for asynchronous tests (like resolving promises). The it functions after the beforeEach are not executed until the done function is called. In this case, done is called after the next tick queue is processed and the then functions of resolved promises have been called.

        describe('and get succeeds', function () {
          var phoneInfo;
          beforeEach(function (done) {
            phoneInfo = {
              country_code: 'US',
              phone_number: '+13145551212',
              national_format: '(314) 555-1212',
                mobile_country_code: '310',
                mobile_network_code: '150',
                name: 'AT&T Wireless',
                type: 'mobile',
                error_code: null,
                mobileCountryCode: '310',
                mobileNetworkCode: '150',
                errorCode: null
              url: '',
              countryCode: 'US',
              phoneNumber: '+13145551212',
              nationalFormat: '(314) 555-1212'

    After get succeeds, the private function sendMessage is called which, in turn, calls the external Twilio API. We add tests for those calls too.

          it('should create new RestClient', function () {
          it('should call RestClient.sendMessage', function () {
              to: phoneInfo.phone_number

    Now sendMessage returns another promise. So we have to do the same thing as we did above. After the message is sent, the code wants to get the message's status until it has been delivered. Since we call the twilio.RestClient constructor a second time, we want to clear the calls using reset.

          describe('and RestClient.sendMessage succeeds', function () {
            var message;
            beforeEach(function (done) {
              message = {
                sid: 'sid',
                status: 'queued',
                body: 'verification code',
                to: phoneInfo.phone_number
            it('should create new RestClient', function () {
            it('should call RestClient.accounts.messages', function () {
            it('should call RestClient.accounts.messages.get', function () {

    Again, RestClient.accounts.messages.get returns a promise. So we have to call resolve on that. Since this is the last external API call, after it is resolved, then the promise returned from sendVerificationCode should be either resolved or rejected. We check this by testing whether or not the success and failure callbacks have been called.

            describe('and RestClient.accounts.messages.get succeeds', function () {
              beforeEach(function (done) {
                message = {
                  sid: 'sid',
                  status: 'delivered',
                  body: 'verification code',
                  to: phoneInfo.phone_number
              it('should succeed', function () {
              it('should not fail', function () {

    We also want to check that the correct value is returned. So we add tests for that. We can examine what was passed to the success callback using callsFor.

              describe('and the results', function () {
                var results;
                beforeEach(function () {
                  results = success.calls.argsFor(0)[0];
                it('should have userInfo', function () {
                    phone: phone,
                    email: email,
                    country: 'US',
                    isActivated: false,
                    isBlocked: false
                it('should have code', function () {
                    string: jasmine.any(String),
                    isVerified: false

    Finally, we'll add a check for the failure case. We'll call reject passing the error value. Just like for resolve, we need to call process.nextTick and use the done function to make the beforeEach asynchronous.

            describe('and RestClient.accounts.messages.get fails', function () {
              var error;
              beforeEach(function (done) {
                error = {
                  message: 'get message failed'
              it('should not succeed', function () {
              it('should fail', function () {
                  message: error.message

    This is just a start. You can see how we'd need to add test cases for when messages().get fails and sendMessage fails. Also, we should test the case where messages().get returns a message status of "undelivered".

    Thanks for reading, and feel free to leave any questions in the comment section below.