AngularJS example MotoAds unit tests

Unit tests result: Application folders tree:
In Angular the controller is separated from the view so it is easy to add the unit tests to MotoAds application. Recall the controller code which will be tested (controllers.js):
motoAdsApp.controller('AdvertsController', ['$scope', 'Brand', 'Country', 'Advert', 
  function($scope, Brand, Country, Advert) {
    $scope.oneAtATime = true;

    $scope.brands = Brand.query();

    $scope.countries = Country.query();

    $scope.sortByCols = [{
        "key": "year",
        "name": "Year"
      }, {
        "key": "price",
        "name": "Price"
      }];

    $scope.adverts = [];
    var allAdverts = Advert.query(filterAdverts);

    $scope.filter = {
      brandName: null,
      modelName: null,
      country: null,
      region: null,
      yearFrom: null,
      yearTo: null
    };

    $scope.isAnyFilter = function() {
      var f = $scope.filter;
      if (f.brandName || f.modelName || f.country || f.region || f.yearFrom || f.yearTo) {
        return true;
      }
      return false;
    };

    $scope.removeAllFilter = function() {
      $scope.filter = {
        brandName: null,
        modelName: null,
        country: null,
        region: null,
        yearFrom: null,
        yearTo: null
      };
    };

    $scope.addBrandModelFilter = function(brand, model) {
      $scope.filter.brandName = brand.name;
      $scope.filter.modelName = model.name;
    };

    $scope.$watch('filter', filterAdverts, true);

    function filterAdverts() {
      $scope.adverts = [];
      angular.forEach(allAdverts, function(row) {
        if (!$scope.filter.country) {
          $scope.filter.region = null;
        }
        if ($scope.filter.brandName && $scope.filter.brandName !== row.brandName) {
          return;
        }
        if ($scope.filter.modelName && $scope.filter.modelName !== row.modelName) {
          return;
        }
        if ($scope.filter.country && $scope.filter.country.name !== row.countryName) {
          return;
        }
        if ($scope.filter.region && $scope.filter.region.name !== row.regionName) {
          return;
        }
        if ($scope.filter.yearFrom && $scope.filter.yearFrom > row.year) {
          return;
        }
        if ($scope.filter.yearTo && $scope.filter.yearTo < row.year) {
          return;
        }
        $scope.adverts.push(row);
      });
    };

  }]);
]]>
We should write the script for running unit tests (test.bat):
@echo off

REM Windows script for running unit tests
REM You have to run server and capture some browser first
REM
REM Requirements:
REM - NodeJS (http://nodejs.org/)
REM - Karma (npm install -g karma)

set BASE_DIR=%~dp0
karma start "%BASE_DIR%\..\config\karma.conf.js" %*
]]>
We configure Karma for our tests (karma.conf.js):
module.exports = function(config){
    config.set({
    basePath : '../',

    files : [
      'app/lib/angular/angular.js',
      'app/lib/angular/angular-*.js',
      'app/lib/ui-bootstrap/ui-bootstrap-*.js',
      'app/lib/angular/angular-mocks.js',
      'app/js/**/*.js',
      'test/unit/**/*.js'
    ],

    exclude: ['app/lib/angular/angular-scenario.js'],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
      'karma-junit-reporter',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine'
    ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

});};
]]>
When everything is ready we can start writing tests in controllersSpec.js.

Step 1
We prepare dummy data:
var BRANDS_RESPONSE = [
  {"name": "Audi", "models": [{"name": "A1"}, {"name": "A3"}]},
  {"name": "Dacia", "models": [{"name": "Duster"}, {"name": "Logan"}]}
];

var COUNTRIES_RESPONSE = [
  {"name": "Germany", "regions": [{"name": "Bavaria"}, {"name": "Hesse"}]},
  {"name": "Poland", "regions": [{"name": "Lesser Poland"}, {"name": "Masovia"}]}
];

var ADVERTS_RESPONSE = [
  {
    "brandName": "Audi",
    "modelName": "A1",
    "year": 2011,
    "price": "35000",
    "imageUrl": "img/audi_a1_1.jpg",
    "countryName": "Germany",
    "regionName": "Bavaria"
  },
  {
    "brandName": "Audi",
    "modelName": "A1",
    "year": 2010,
    "price": "30000",
    "imageUrl": "img/audi_a1_2.jpg",
    "countryName": "Poland",
    "regionName": "Masovia"
  },
  {
    "brandName": "Dacia",
    "modelName": "Duster",
    "year": 2012,
    "price": "33000",
    "imageUrl": "img/dacia_duster_1.jpg",
    "countryName": "Poland",
    "regionName": "Masovia"
  },
  {
    "brandName": "Dacia",
    "modelName": "Duster",
    "year": 2009,
    "price": "27000",
    "imageUrl": "img/dacia_duster_2.jpg",
    "countryName": "Germany",
    "regionName": "Bavaria"
  }];
]]>

Step 2
Load modules, define dummy services, initialize scope, controller and filter:
describe('MotoAds controllers', function() {
  beforeEach(module('motoAdsApp'));
  beforeEach(module('ui.bootstrap'));
  beforeEach(module('motoAdsServices'));

  describe('AdvertsController', function() {
    var scope, ctrl, $httpBackend, orderByFilter;

    beforeEach(inject(function(_$httpBackend_, $rootScope, $controller, $filter) {
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('data/brands.json').respond(BRANDS_RESPONSE);
      $httpBackend.expectGET('data/countries.json').respond(COUNTRIES_RESPONSE);
      $httpBackend.expectGET('data/adverts.json').respond(ADVERTS_RESPONSE);
 
      orderByFilter = $filter('orderBy');

      scope = $rootScope.$new();
      ctrl = $controller('AdvertsController', {$scope: scope});
    }));

    // TEST WILL BE HERE

  });

});
]]>

Step 3
We write first test - filtering by brand and model:
    it('should filter by brand and model', function() {
      expect(scope.adverts).toEqual([]);
      $httpBackend.flush();
      expect(scope.adverts.length).toBe(4);

      scope.$apply(function() {
        scope.filter = {
          brandName: "Audi",
          modelName: "A1",
          country: null,
          region: null,
          yearFrom: null,
          yearTo: null
        };
      });

      expect(scope.adverts.length).toBe(2);
    });
]]>

Step 4
We write second test - filtering by country and region:
    it('should filter by country and region', function() {
      expect(scope.adverts).toEqual([]);
      $httpBackend.flush();
      expect(scope.adverts.length).toBe(4);

      scope.$apply(function() {
        scope.filter = {
          brandName: null,
          modelName: null,
          country: {name: "Germany"},
          region: {name: "Bavaria"},
          yearFrom: null,
          yearTo: null
        };
      });

      expect(scope.adverts.length).toBe(2);
    });
]]>

Step 5
We write third test - filtering by year:
    it('should filter by from yearFrom to yearTo', function() {
      expect(scope.adverts).toEqual([]);
      $httpBackend.flush();
      expect(scope.adverts.length).toBe(4);

      scope.$apply(function() {
        scope.filter = {
          brandName: null,
          modelName: null,
          country: null,
          region: null,
          yearFrom: 2011,
          yearTo: 2012
        };
      });

      expect(scope.adverts.length).toBe(2);
    });
]]>

Step 6
We write fourth test - sorting by year:
    it('should sort by year', function() {
      expect(scope.adverts).toEqual([]);
      $httpBackend.flush();
      expect(scope.adverts.length).toBe(4);

      var sortedAdverts = orderByFilter(scope.adverts, "year");

      var prevYear = 0;
      for (var i = 0; i < sortedAdverts.length; i++) {
        var advert = sortedAdverts[i];
        expect(advert.year).toBeGreaterThan(prevYear);
        prevYear = advert.year;
      }
    });

Step 7
We run unit test in Node.js command prompt by running test.bat and we should see:
D:\devhome\github\motoads>scripts\test.bat
INFO [karma]: Karma v0.10.4 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 30.0.1599 (Windows Vista)]: Connected on socket QZD2lLpZ-KlcZE2pqcrE
Chrome 30.0.1599 (Windows Vista): Executed 4 of 4 SUCCESS (0.517 secs / 0.076 secs)
]]>

If you want to run this example on your computer, you can download sources from GitHub.

Any comment would be highly appreciated.

Komentarze

Popularne posty z tego bloga

Java ESL program for connecting to the FreeSWITCH

AngularJS example MotoAds more advanced than the tutorial on angularjs.org

Java program for connecting to the FreeSWITCH XML-RPC