Chapter 7. Troubleshooting and Best Practices

This chapter focuses on the troubleshooting libraries and tools for AngularJS. In this chapter, we will also discuss unit testing of the AngularJS application using Jasmine and Karma. At the end of this chapter, we will go through the AngularJS coding best practices. This chapter is organized as follows:

  • Troubleshooting libraries and tools
  • Unit testing
    • Unit testing using AngularJS
    • Unit testing using Jasmine
    • Unit testing using Karma
  • Angular JS coding best practices
    • Organizing code
    • Using AngularJS

Troubleshooting libraries and tools

JavaScript is a dynamic computer programming language. It is mostly used for client-side application development and as part of web browsers. JavaScript communicates asynchronously with clients, such as web browsers and it also controls the client. JavaScript can be used with server-side programming with the runtime environment, for example, Node.js and AngularJS.

JavaScript is categorized as a prototype-based scripting language using dynamic typing. Prototype-based programming is an elegant object-oriented programming in which we can reuse behavior, which is known as inheritance. JavaScript can also be used in other environments along with web browsers, such as PDF documents, site-specific browsers, and desktop widgets.

JavaScript is a typed language and has great power of manifestation, but there is no help from the compiler for JavaScript. The JavaScript code is executed through interpretation instead of compilation. The AngularJS framework provides many features to test the application created using the AngularJS framework. There are also a few libraries and tools available to test the applications created with the AngularJS framework. For example, Jasmine, Karma, Grunt, Bower, and Yeoman, explained as follows:

  • Jasmine: This is a test-driven development framework for JavaScript applications. It is the furthermost widespread choice to test applications created with AngularJS. Jasmine provides different functions in order to structure tests and make proclamations.
  • Karma: This is a command-line tool that can be used to offspring a web server that loads the client-side application source code and executes the test. We can run Karma with different types of browsers. Karma is a Node.js application, which can be installed through npm.
  • Grunt: This runs the tasks that help to automate monotonous tasks, for example, testing and setting up a preview of AngularJS applications.
  • Bower: This helps to install all the AngularJS application dependencies, for example, CSS, Bootstrap, and the other JavaScript framework and libraries. Bower is a package manager.
  • Yeoman: This is a tool that contains three core components: Grunt, Bower, and the scaffolding tool, Yo. With the help of scaffolding templates, Yo generates the boilerplate code. It automatically configures Grunt and Bower for the application. Yeoman provides templates for almost all JavaScript frameworks, such as AngularJS, Backbone, Ember, and so on.

Unit testing

In computer programming or application development, unit testing is a technique by which distinct units of applications are tested to determine whether they are working as expected, according to the user's requirement. Preferably, each unit test is sovereign from each other. Replacements for unit test, such as functions, objects, and test data, which are configured to test a unit by running it under every condition and monitoring its behavior and output, are known as test harness. For example, to test a function, we need to verify the given set of inputs, we have to conclude whether the function is returning the proper output values, and will elegantly handle failures during the execution. Unit test is usually written and tested by developers to confirm that the code meets its design and behaves as envisioned.

The goal of unit testing is to ensure that each part of the application is correct. A unit test delivers a firm written contract that the piece of code must satisfy. As a result, it gives numerous benefits, which are described as follows:

  • Unit testing eventually helps us to detect failures in the application and/or logic to help improve the quality of the code that creates a certain function.
  • Unit test requires the application code to be easily testable, which means that your code must support this individual type of valuation. As such, applications are more likely to have a greater number of smaller, more intensive functions that provide a single operation on a set of data rather than having large functions perform a number of different operations.
  • Writing compact unit tests and well-tested applications helps us to prevent breaking the application. As we are testing the functionality of applications, we are developing test cases that can be executed each time the function is invoked. Whenever the failure happens, we know that there is something to be addressed.

Unit testing is ought to be an exhausted conjunction with different libraries or tools, such as Jasmine and Karma. They will solely show the prevalence or absence of specific errors; they can't prove a whole absence of errors. So as to ensure correct behavior for each execution path and each attainable input, and to make sure that errors are absent, different technique's area units are needed, particularly to apply formal strategies to prove that a library and tool part has no sudden behavior.

The following figure shows the process of unit testing:

Unit testing

What and what not to test

The AngularJS framework has become one of most popular frameworks to develop single-page applications. The AngularJS framework's unique approach to HTML compiling and two-way data binding makes it an operative tool to develop client-side applications competently. The AngularJS framework is defined by modules, such as directives, controllers, services, filters, and factories. Each module in the AngularJS framework is able to communicate with another through its external interface. All of these modules also share a common attribute. These modules behave as black boxes, which means that these modules have an inner behavior and outer interface materialized by input and outputs. This is precisely what unit tests are for: to test a module's outer interfaces.

The following points show what and what not to test:

  • With unit testing, we cannot catch every error in the application because we cannot evaluate every execution path, only the most significant functions.
  • Unit test only tests the functionality of the units, and it will not find integration errors or broader system level errors.
  • Unit testing is a combinatorial problem, such as every Boolean decision statement requires at least two tests. One with the outcome as true and other with an outcome of false.
  • In unit testing, it is indispensable to keep records of the tests performed, as well as all the changes that have been made to the application code.

Unit testing using AngularJS

In the AngularJS framework, several types of objects can be declared in modules, such as filters, services, factories, and so on. In this section, we will discuss how these object types can be bootstrapped and tested in AngularJS.

Module

AngularJS's filters, services, and factories are easy to test because they need very few preparations, which are usually other services. AngularJS links services to other services or objects using the very communicative reliable injection model, which is basically the same as asking a function's parameters. The way of injecting things in test cases in AngularJS is super easy and simple. The following code shows how you can test the AngularJS object factory:

var app = angular.module('myApp', [])

app.factory('myFactory', ['$log', function($log) {

  return {

    warning: function() {

      $log.warn('Warning message'),

    }

  };

}]);

Factory test unit code is as follows:

describe('myApp', function() {

  beforeEach(module('myApp'));

  var Factory;
  var $log;

  beforeEach(inject(function(_myFactory _, _$log_) {
    Factory = _ myFactory_;
    $log = _$log_;
    sinon.stub($log, 'warn', function() {});
  }));
  describe('when invoked', function() {

    beforeEach(function() {
      Factory.ook();
    });

    it('should say Ook', function() {
      expect($log.warn.callCount).to.equal(1);
      expect($log.warn.args[0][0]).to.equal('Ook.'),
    });
  });
});

As seen in the preceding code, the $log service is injected into the factory called myFactory. In the AngularJS framework, the $log service is a service to log. It writes the message into the client browser's console. The main advantage of these services is to perform debugging and troubleshooting of AngularJS applications.

The AngularJS $log service has the following methods:

  • log: This method writes the log message
  • info: This method writes an information message
  • warn: This method writes a warning message
  • error: This method writes an error message
  • debug: This method writes a debug message

Controller

The following code shows the use of the $log services in the AngularJS controller:

var app=angular.module('logExample', []); 

app.controller('ctrlLog', function($scope, $log) { 

$scope.$log = $log; 
$scope.message = 'Hello World!'; 

});

The following is the DOM implementation:

<div ng-controller="LogController"> 

<p>Reload this page with open console, enter text and hit the log button...</p> 

Message: <input type="text" ng-model="message"/> 

<button ng-click="$log.log(message)">log</button> 

<button ng-click="$log.warn(message)">warn</button> 

<button ng-click="$log.info(message)">info</button> 
<button ng-click="$log.error(message)">error</button> 

</div>

The following screenshot shows the output of the DOM:

Controller

As shown in the preceding screenshot, by clicking on the button, you will see the following in the browser's console:

Controller

In the preceding screenshot, we used the Internet Explorer browser, and by clicking on the F12 key, open the develop tool of the browser, and then click on Console. By clicking on each button on the page, we will get the previous output:

Directive

An AngularJS directive is basically a function that executes once the AngularJS compiler finds it within the DOM. The function(s) will do nearly anything, which is why it is hard to define what a directive is. In other words, an AngularJS directive is a new way to encapsulate the HTML in the DOM. Every directive contains a name (such as ng-repeat, tabs, your-own-directive) and every directive determines where it is often used, such as in the DOM element, attribute, or class.

In the following code, a directive displays text and a button inside the div element:

var app = angular.module('myApp', [])

app.directive('myUnitTestDirective', function() {
  return {
    scope: {label: '=', callback: '&onClick'},
    replace: true,
    restrict: 'E',
    link: function(scope, element, attrs) {

    },
    template: '<div>' +
      '<div>{{label}}</div>' +
      '<button ng-click="callback()">Click me!</button>' +
      '</div>'
  };
});

For the preceding code, we would like to test whether the HTML element label has properly passed to the div element and to verify that the click event is fired when the button is clicked.

The following is the directive unit test code:

describe('directives', function() {

  beforeEach(module('myApp'));

  var element;
  var outerScope;
  var innerScope;
  beforeEach(inject(function($rootScope, $compile) {

    element = angular.element('<super-button label="myLabel" on-

click="myCallback()"></super-button>'),

    outerScope = $rootScope;
    
    $compile(element)(outerScope);

    innerScope = element.isolateScope();

    outerScope.$digest();

  }));

  describe('label', function() {
    
  beforeEach(function() {

      outerScope.$apply(function() {
      
      outerScope.myLabel = "Hello world.";

      });
    })

    it('should be rendered', function() {
      
    expect(element[0].children[0].innerHTML).to.equal('Hello 

    world.'),

});

});

  describe('click callback', function() {
    
   var mySpy;

    beforeEach(function() {
   
    mySpy = sinon.spy();
    outerScope.$apply(function() {
        
    outerScope.myCallback = mySpy;

  });

  });

    describe('when the directive is clicked', function() {
      
    beforeEach(function() {
        
    var event = document.createEvent("MouseEvent");
        
        event.initMouseEvent("click", true, true);
        
        element[0].children[1].dispatchEvent(event);
      
      });

      it('should be called', function() {
      
      expect(mySpy.callCount).to.equal(1);

  });

});

});

});

In the preceding code, when we click on the Click me! button, the function is passed as the on-click attributes are called. If we look closely at the directive, we can find out that the function gets locally renamed to callback. It will be published under this name on the directive's isolated scope.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset