Mocking Services for AngularJS tests

One of the things that I’ve enjoyed more in the last year is Test Driven Development, with the satisfaction of seeing the green after all tests have passed. AngularJS is written with testability in mind which makes testing applications easy. And although it can be hard to grasp at the beginning, there a lot of good resources around to learn how to unit test the application logic.

But in this post I want to focus in writing mock objects. In previous versions of the AngularJS generators for Yeoman there was a mock folder generated inside of the test directory. This made me think on a way to write separated files for every necessary mock so we can include them where required. With the new structure recommendations this mock folder has been removed, but the point of writing separate files it’s the same.

Let’s say we have a complex Auth service to deal with user authentication.

angular.module('webApp')
  .factory('Auth', function($http) {
    var currentUser = {};    
    return {
      login: function(credentials) {
        $http.post('/api/auth', credentials).then(function(response) {
          currentUser = response.data.user;
        });
      }, 
      logout: function() {
        currentUser = {};
      },
      isLoggedIn: function() {
        return !!currentUser.id;
      } 
    };
  });

This service could be injected in a controller to submit a form just if the user is logged in.

angular.module('webApp')
  .controller('ContactCtrl', function(Auth) {
    this.showAlert = false;
    this.submitted = false;
    this.submitForm = function() {
      // If the user is not logged-in, return and display the alert.
      if (!Auth.isLoggedIn()) {
        this.showAlert = true;
        return;
      }
      // Handle form submit.
      this.submitted = true;     
    };   
  });
<div ng-controller="ContactCtrl as contact">
    <form name="contactForm" ng-submit="contact.submitForm()">
        <button type="submit">Submit</button>
        <div class="alert" ng-if="contact.showAlert">
            Please <a href="/login">login</a>
        </div>
    </form>
</div>

Note that for the sake of example, the code of the service has been reduced to a minimum while the form controller it’s pretty dumb; this way we can just get the idea of the functionality and focus in writing the mock object.

How to mock our Service?

Another long topic in AngularJS is the difference among Factory, Service, Value, Constant and Provider. But for creating the mock we just need to know that these five types are built on top of Provider, so the injector is going to look for an AuthProvider.

To write our Provider we don’t need to use $http to interact with the backend, as we just need to emulate the functionality.

angular.module('authMock', [])
  .provider('Auth', function() {
    this.userLoggedIn = false;
    this.$get = function() {
      return  {
        login: function() {
          this.userLoggedIn = true;
        },
        logout: function() {
          this.userLoggedIn = false;
        },
        isLoggedIn: function() {
          return this.userLoggedIn;
        }
      };
    };
  });

So we can unit test our Controller injecting the mocked Service.

describe('Controller: ContactCtrl', function() {

  // Load the controller module.
  beforeEach(module('webApp'));
  // Load the mock service module.
  beforeEach(module('authMock'));
    
  var ContactCtrl, Auth;
  // Initialize the controller and the mocked service.
  beforeEach(inject(function($controller, _Auth_) {
    Auth = _Auth_;
    ContactCtrl = $controller('ContactCtrl', {
      Auth: Auth
    });
  }));
    
  it('should submit the form if the user is logged in', function() {
    Auth.login();
    ContactCtrl.submitForm();
    expect(ContactCtrl.submitted).toBe(true);
  });
    
  it('should not submit the form if the user is not logged in', function() {
    Auth.logout();
    ContactCtrl.submitForm();
    expect(ContactCtrl.submitted).toBe(false);
  });
  
});

I have put all this code together on a JSFiddle so you can see the specs passing.

Tagged , , ,

AngularJS Capitalize Filter

Over a month ago I was messing around with CSS when I realized the text-transform property with the capitalize keyword works just with lowercase text, as it forces the first letter of each word to be converted to uppercase keeping the rest of the characters unchanged.

In the current project I’m working on I’m getting uppercased text from the Rest API so this was the perfect scenario to create my first AngularJS filter. Moreover, as we are working with team names, we need a special letter case where team abbreviations are displayed like CD Logroñés or FC Barcelona.

It was really simple for me to create this filter and I decided to open source it as it might be helpful for somebody else who needs capitalization or just as a base for developing a custom filter.

angular.module('angular-capitalize-filter', [])
  .filter('capitalize', function() {
    return function (input, format) {
      if (!input) {
        return input;
      }
      format = format || 'all';
      if (format === 'first') {
        // Capitalize the first letter of a sentence
        return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
      } else {
        var words = input.split(' ');
        var result = [];
        words.forEach(function(word) {
          if (word.length === 2 && format === 'team') {
            // Uppercase team abbreviations like FC, CD, SD
            result.push(word.toUpperCase());
          } else {
            result.push(word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
          }
        });
        return result.join(' ');
      }
    };
  });

For now it has just three options: capitalize all the words in a sentence, capitalizes just the first word of a sentence and the special one for team names.

You can feel free to install it using Bower or fork it from the repository.

> bower install angular-capitalize-filter
Tagged , , ,

Holiday alert module for PrestaShop

Last year I got a client that needed me to help them with the technical part of the e-commerce PrestaShop. Although I didn’t use PHP for over a year I accepted the challenge and for Christmas they wanted to show an alert to their clients because the orders wouldn’t be processed till January.

This was a good chance to dig into the process of creating a PrestaShop module and I implemented a simple module that shows a message in the top of the e-shop or in the shopping cart page.

For the layout of the box I’m using the one of Bootstrap so you can choose among four colours. This can be configured in the back-end as well as the message of the alert.

Today I have created a GitHub repository where I’m sharing the code of this module and hopefully soon there will be others.

Let me know how it goes if you use it!

Tagged , , ,

Using Grunt to run Mocha tests with Backbone.js and RequireJS

I don’t have all the time that I would like for writing here but before the end of the year it’s time for a new blog post.

This year has been really prolific for me learning new things with JavaScript and in the last months I have adopted the Yeoman workflow, which really helps to develop web applications.

One of the tools included in Yeoman is Grunt, a task runner that is used to build, preview and test the project.

In the process of converting a Backbone.js + RequireJS app to this Yeoman workflow, I had a hard time figuring the right configuration to run tests both in the command line and the browser. There are several blog posts and repositories talking about it but non of them worked for me so I’m summarizing my experience here.

I’m assuming you have created an application using Yeoman as follows:

> mkdir app
> cd app
> yo backbone

So the mocha task in the Gruntfile.js is the default one but the run parameter has to be set up to false. You can change the mocha reporter as you like and add as well options for logging so you can track any error.

        mocha: {
            all: {
                options: {
                    log: true,
                    reporter: 'Spec',
                    run: false,
                    timeout: 10000,
                    urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
                }
            }
        },

I added Mocha and Chai as developer dependencies so they can be downloaded with the package manager Bower, which is another Yeoman tool. After this you can remove the lib folder inside of the test folder.

{
  "name": "backbone-app",
  "version": "1.0.0",
  "dependencies": {
    "jquery": "~1.9.0",
    "underscore": "~1.4.3",
    "backbone": "~1.0.0",
    "requirejs": "~2.1.5",
    "requirejs-text": "~2.0.5"
  },
  "devDependencies": {
      "mocha": "*",
      "chai": "*"
  }
}

Another key point was including the test framework and the assertion library inside of script tags in the HTML code and not trying to load them using RequireJS.

<!doctype html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Mocha Spec Runner</title>
    <link rel="stylesheet" href="bower_components/mocha/mocha.css">
</head>
<body>
    <div id="mocha"></div>

    <script src="bower_components/mocha/mocha.js"></script>
    <script>mocha.setup('bdd')</script>
    <script src="bower_components/chai/chai.js"></script>
    <script>var expect = chai.expect</script>

    <script data-main="spec/runner" src="bower_components/requirejs/require.js"></script>
</body>
</html>

Then I created a spec runner which is the main entry point for RequireJS to load the test files and its configuration.

'use strict';
require.config({
    baseUrl: 'scripts/',
    paths: {
        jquery: '../bower_components/jquery/jquery',
        backbone: '../bower_components/backbone/backbone',
        underscore: '../bower_components/underscore/underscore'
    },
    shim: {
        underscore: {
            exports: '_'
        },
        jquery: {
            exports: '$'
        },
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        }
    }
});

var specs = [
    'spec/collections.examples.js',
    'spec/models.example.js'
];

require(specs, function() {
    mocha.run();
});

Now you can create a test file for a Backbone collection like this:

/*global define, describe, it, expect */
'use strict';
define(function(require) {
    var ExamplesCollection = require('collections/examples');

    describe('Examples collection', function() {

        var examples = new ExamplesCollection();

        it('should exist', function() {
            expect(examples).to.exist;
        });

        it('should be an instance of Examples collection', function() {
            expect(examples).to.be.an.instanceof(ExamplesCollection);
        });

    });

});

To run the tests through the command line you just need to run grunt test. If you want to run them in the browser load http://localhost:9001/index.html after typing grunt server:test.

Don’t forget as well to run bower install to download the dependencies and check that the app folder is being mounted for the test task.

            test: {
                options: {
                    port: 9001,
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, 'test'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },

Merry Christmas and Happy Coding!

Tagged , , , , ,

Testing email sending in Django

During the past three months I have developed my first two websites in Python using the Django framework.

Following the tutorial was a great start and it didn’t take me that long to get adapted to Python coming from a PHP background. But when you want to go further the documentation is rather vague and confusing in my opinion.

This week I got to the point where I wanted to test the email functionality. Django offers quite a nice tool while you are developing a website; if you want to see the output of your emails, you can just write them to a file adding a couple of settings.

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = os.path.join(PROJECT_ROOT, 'tmp')

In addition to this I wanted to check the email configuration before going production, but already using the final parameters for the required settings:

EMAIL_HOST=''
EMAIL_HOST_PASSWORD=''
EMAIL_HOST_USER=''
EMAIL_PORT = 25
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = ''
SERVER_EMAIL = ''

The problem is that when Django sets up the testing environment, it changes the email backend to the one in-memory. Fortunately, this behavior can be overridden so you can run a test case with the following code to check if your mail is being actually sent.

 
from django.test import TestCase
from django.core import mail
from django.test.utils import override_settings

@override_settings(EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend')
class EmailTest(TestCase):       
    def test_send_email(self):
        mail_sent_success = mail.send_mail('Subject here', 
                       'Here is the message.',
                       '', ['pablo@esebesoftware.com'],
                       fail_silently=False)
        self.assertEquals(mail_sent_success, 1)

Notice that DEFAULT_FROM_EMAIL is used if omitting the from_email field.

Tagged , , ,
Follow

Get every new post delivered to your Inbox.

Join 1,038 other followers