Testing Two-Way Data Binding in AngularJS

Two-way data binding is one of the strongest capabilities of AngularJS.

Testing Two-Way Data Binding

Once you accumulate controllers, directives, and begin to use various libraries such as High Charts, you need to test that indeed two-way data binding works across all these constructions.

Testing data-binding with explicit user actions such as filling input boxes, or clicking buttons is cumbersome and requires littering your web application screens with testing artifacts.

The following will describe some cleaner techniques.

Periodic Data Change

One alternative approach, that we used in several projects is to have your controller/directive code modify your data periodically with an $interval (AngularJS setInterval):

$interval(function(){
    $scope.value = Math.random(100);
}, 300);

where in your ux, you have:

There is nothing so relieving as seeing the data changing before your eyes ever so often.

When using libraries, such as visualization libraries, we need to use structured data such as a time series. In such a case, we periodically generate random data, such as:

$scope.a = [10.2, -0.45, 5.6, 2.3, -1.8, 7.2, 8.5];     
$interval(function(){
    $scope.value = _.sample($scope.a);
}, 300);

where we use the Underscore sample function to sample the array.

The data is then used with High Charts as:

where in our controller we have:

$scope.chartConfig = {

    // many options omitted here

    //Series objects
    series: [{
          data: $Scope.a,
          type: 'spline'
    }]
};

And we periodically change the data in a random way with:

$interval(function(){
    $scope.chartConfig.series.splice(0,1, {
        data: _.sample($scope.a),  type: 'spline'
    });
}, 300);

Watches

On the flip side, when directives involving form elements, such as an input element are used, we need to test that two-way data binding works across a call chain of directives and controllers.

If you have a directive using an input element:

myApp.directive('myInput', function () {
    return {
           restrict: 'A',
        replace: true,
        scope: {
            field: '=' //Two-way data binding
        },
        template: ''
    };
});

used as:

To test for two-way data binding, we would place a watch on the field given to the controller,

myApp.controller('myController', ['$scope', '$window', function ($scope, $window) {
    $scope.username = 'johndoe';

    $scope.$watch('username', function(newValue, oldValue) {
        $window.alert("new input:" + newValue);
    });
}]);