On testing web application across multiple environments

Users have the freedom of choosing which browser to access the web. Add to that the variety of operating systems and the potential need of also making web pages layout compatible (responsive) with devices like tablets and smartphones and we get the picture of how laborious and challenging performing functional tests for a web application might become.

Deciding how many variations to regard on testing might depend on different goals – whether to target a specific niche, to initially comprise only the combinations being used for most users or something else. Regardless, testing is constrained by the resources available:

  • Budget
  • People: the number of team members fully or partly dedicated (developers who also build the application) to tests and their skills
  • Infrastructure to run tests
  • Schedule: how much time can be allocated to working on tests

Starting really simple, developers can test on browsers they have available in their workstations. Depending on the project strategy, that might be enough. For instance, a startup building its first MVP might want not even know whether the ideas being put in place will be well accepted by the market, so collecting feedback quickly becomes key to determine whether to continue or to quit (Fail Fast approach).

Chrome, Firefox and Safari are available for different desktop and mobile Operating systems, potentially comprising a very large set of users. Even without a Windows environment, teams can test on Internet Explorer using the virtual machines Microsoft made available – see this link.

For mobile, tests can be performed on real devices like tablets and smartphones or through simulators – there are some options for Android; for IOS, there is Xcode, available in macOS only, comprising a good set of Apple devices. There is also the option of using responsive mode of desktop browsers, where the screen dimensions can be reduced to mimic a device.

As the number of tests increase, and environment variations to run those on, automation becomes key. Selenium is one of the tools available for automating functional tests, being capable of interacting many different browsers. It is free and it provides APIs that are already being used by a large number of programming languages.

By the way, in my current project, I use Nighwatch.js, a Node.js based tool that invokes the Selenium APIs and provides a framework for defining and running test scripts.

To comprise different build versions of browser, plus the variety of operating systems, more machines (physical and virtual) can be setup to cover a set of variations. By the way, Selenium also has a subproject called Selenium Grid,  that comes with tooling for constructing a grid of machines for performing tests.

Or, rather than building machines, there are also (paid) services on Cloud with infrastructure ready to be provisioned on demand, setup to run functional tests in different browsers and operating systems. Sauce Labs and BrowserStack are two examples of such services. The level of test execution concurrency and frequency is determined by what plan, among many being offered by those services, can be afforded by the team.

Besides the tools mentioned here, surely there are many others. Feel free to leave comments.

Promise chaining – for a cleaner code in AngularJS

There are cases when there is the need to perform a set of asynchronous operations, one after another, to load or process something. In AngularJS, If the result of each operation is wrapped by a promise, taking advantage of promise chaining leads to code easier to be read and maintained.

I do remember of my beginning on using promises. I had a glance at documentation and some examples and got acquainted to its basics – the successful and error callbacks and so on.

Then I had to write something to load a complex resource, by walking over a sort of tree structure, in order to feed in some form. Given all that I knew so far, I wrote what seemed intuitive to me:

  • Load the first resource and have its result as a promise
  • On the success callback for that promise, process the first resource and load the second resource, to have a second promise
  • And so on….

The code looked like this:

resourceService.loadResourceOne().then(
    function (resourceOne) {
        var inputForResourceTwo = computeInputForResource(resourceOne);

        resourceService.loadResourceTwo(inputForResourceTwo).then(
            function (resourceTwo) {
                var inputForResourceThree = computeInputForResource(resourceTwo);

                resourceService.loadResourceThree(inputForResourceThree).then(
                    function (resourceThree) {
                        // Successful end

                        console.log('Resource found: ' + resourceThree);
                    },
                    function (errorLoadingResourceThree) {
                        handleError(errorLoadingResourceThree);
                    }
                )
            },
            function (errorLoadingResourceTwo) {
                handleError(errorLoadingResourceTwo);
            }
        )
    },
    function (errorLoadingResourceOne) {
        handleError(errorLoadingResourceOne);
    }
);

I started to get annoyed when I had to revisit my code, to load even more resources. The increasing nesting of blocks, and the need to scroll the screen up and down to see the success and error handling for an intermediate promise really called my attention. I realized I should look for some refactoring.

By AngularJS design, a promise callback can return another callback, so the load of resources could be done in adjacent blocks of code. That is the so called promise chaining.

The load of three resources, now as a chain:

resourceService.loadResourceOne().then(
    function (resourceOne) {
        var inputForResourceTwo = computeInputForResource(resourceOne);

        // returns promise to the next step in the chain
        return resourceService.loadResourceTwo(inputForResourceTwo);
    }
).then(
    function (resourceTwo) {
        var inputForResourceThree = computeInputForResource(resourceTwo);

        return resourceService.loadResourceThree(inputForResourceThree);
    }
).then(
    function (resourceThree) {
        // Successful end

        console.log('Resource found: ' + resourceThree);
    }
).catch(
    function (error) {
        handleError(error);
    }
);

The handling of a promise is well contained in a block of code, returning the promise to be handled by the next callback in the chain. Further needs to add loading of more resources no longer require nesting of code.

Error handling remained in a single block of code as well, as the last step in the chain, to capture a failure for any promise in the chain. By design, whenever there is a promise rejection, AngularJS looks for the first error callback in the chain – in the example, it is the catch block, which is basically a syntactic sugar for:

.then (null, function (error) {
        handleError(error);
    }
)

Code looks better. After all, a bigger plus in both readability and maintainability – a potential for fewer bugs.

 In such a mood for refactoring, a next step could be done:

function computeInputAndLoadResourceTwo(resourceOne) {
    var inputForResourceTwo = computeInputForResource(resourceOne);

    // returns promise to the next step in the chain
    return resourceService.loadResourceTwo(inputForResourceTwo);
}

function computeInputAndLoadResourceThree(resourceTwo) {
    var inputForResourceThree = computeInputForResource(resourceTwo);

    return resourceService.loadResourceThree(inputForResourceThree);
}

function printResourceThree(resourceThree) {
    // Successful end

    console.log('Resource found: ' + resourceThree);
}

resourceService.loadResourceOne()
    .then(computeInputAndLoadResourceTwo)
    .then(computeInputAndLoadResourceThree)
    .then(printResourceThree)
    .catch(handleError);

Each callback in is now defined as a named function, turning the chain definition much slimmer and making the code self documenting.

It is not the case here, but such an approach also enables reuse of a same function in more than one step in the chain.

References

AngularJS documentation: $q service

AngularJS promises as cache objects

Caching is a handy approach for keeping values that take a good amount of time to be computed.

Given the design in AngularJS, a promise can serve the purpose of being the cache object, so that it’s able to address as many invocation to return the cached value, saving some lines of code.

To exercise that concept, an application to show lucky numbers below

1st value Show me
2nd value Show me
3rd value Show me

Each lucky number is revealed on the user click to the corresponding Show me button.

Suppose all lucky numbers are provided by a third party service. All numbers are computed at once, but that operation takes some time to complete. To improve user experience, all lucky numbers are preloaded and cached, so that the user click on any button can have instant effect on the screen

Some HTML code:

<body ng-app="LuckyNumbersApp">

<table ng-controller="LuckyNumbersValuesCachingCtrl">
    <thead>
        <tr>
            <th colspan="3">Lucky numbers - caching values</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1st value</td>
            <td>{{luckyNumbers[0]}}</td>
            <td>
                <button ng-click="getLuckyNumber(0)">Show me</button>
            </td>
        </tr>
        <tr>
            <td>2nd value</td>
            <td>{{luckyNumbers[1]}}</td>
            <td>
                <button ng-click="getLuckyNumber(1)">Show me</button>
            </td>
        </tr>
        <tr>
            <td>3rd value</td>
            <td>{{luckyNumbers[2]}}</td>
            <td>
                <button ng-click="getLuckyNumber(2)">Show me</button>
            </td>
        </tr>
    </tbody>
</table>

And the AngularJS counterpart:

var appModule = angular.module('LuckyNumbersApp', []);

appModule.controller('LuckyNumbersValuesCachingCtrl', function ($scope, $q, luckyNumbersService) {
    //  The values retrieved from outside the application are cached
    var cachedValues;
    $scope.luckyNumbers = [];

    function getAllLuckyNumbers() {
        var newPromise = $q.defer();

        if (cachedValues) {
            newPromise.resolve(cachedValues);
        }
        else {
            luckyNumbersService.computeLuckyNumbers().then(function (data) {
                cachedValues = data;
                newPromise.resolve(data);
            });
        }

        return newPromise.promise;
    }

    $scope.getLuckyNumber = function (num) {
        console.log('[Values caching] Loading lucky number ' + (num + 1));
        getAllLuckyNumbers().then(function (result) {
            console.log('[Values caching] Lucky number ' + (num + 1) + ' : ' + result[num]);
            $scope.luckyNumbers[num] = result[num];
        });
    };

    getAllLuckyNumbers();
});

The retrieval the numbers from the service (luckyNumbersService) is implemented inside function getAllLuckyNumbers, which extracts the array of numbers from a promise returned by service and wraps the array in a new promise, that is returned to the function caller. The array is kept in cachedValues variable, to avoid more invocations to the service in further function calls. Each number can then consumed, to populate the screen: user clicks on button, getLuckyNumber function is called, then getAllLuckyNumber is called (if the preloading of numbers has already completed, all calls here would here will already be handled by the cache).

So far, so good. It all works.

A fact: the way promises were designed in AngularJS allow the then method to be called many times. Documentation already gives in a hint (On the initial description of then method: This method returns a new promise…) and I confirmed that by looking at the $q service source code. In order not to extend this post too much, I won’t go deep.

Having that said, rather than caching the array of numbers, extracted from the promise returned by luckyNumbersService, the promise itself can be cached, so the code can be polished a bit.

A second version of the controller:

appModule.controller('LuckyNumbersPromiseCachingCtrl', function ($scope, $q, luckyNumbersService) {
    // Simpler approach: the promise itself returned by the service is cached
    var cachedPromise;
    $scope.luckyNumbers = [];

    function getAllLuckyNumbers() {
        if (!cachedPromise) {
            cachedPromise = luckyNumbersService.computeLuckyNumbers();
        }

        return cachedPromise;
    }

    $scope.getLuckyNumber = function (num) {
        console.log('[Promise caching] Loading lucky number ' + (num + 1));
        getAllLuckyNumbers().then(function (result) {
            console.log('[Promise caching] Lucky number ' + (num + 1) + ' : ' + result[num]);
            $scope.luckyNumbers[num] = result[num];
        });
    };

    getAllLuckyNumbers();
});

Now, getAllLuckyNumbers caches and forwards the promise returned by the service. The rest of the code stays the same, where each user click to a button makes the corresponding numbers provided by the service to populate the screen. And for multiple button clicks, the promise instance cached by the controller will be able to handle each then call made inside getLuckyNumber, invoking the callback with the array of numbers computed by the service.

Even if getAllLuckyNumbers had to create a new promise, to wrap result of perhaps some transformation, validation, filtering or any other operation over the numbers provided by the service, that promise could be kept and reused later.

For a running version of this example, access this link

References

AngularJS documentation: $q service

A custom AngularJS directive to add lazy loading (infinite scroll) capability to an HTML table

For scenarios where the amount of data to be displayed in a table is likely to affect the page load, a nice enhancement is to have the data split in smaller pieces, being retrieved and rendered according to the way the user interacts with the table.

The user experience will be quite similar to what Facebook and Twitter have in their timeline pages. On the page load, the table will have just a subset of the data. The user can scroll the table, but before such a scrolling hits the bottom, the table content is augmented with another data subset. The user is then able to scroll over that new subset and the process of presenting data sets goes on.

This main purpose of this blog entry is to describe how to implement such a lazy loading capability (also referred as infinite scroll) in AngularJS, taking advantage of custom directives and some JQuery functions.

To ilustrate the concept, let us regard a table that displays historical prices of a fictitious stock.

A running version of the example can be accessed in this link

Below, the HTML code, along with AngularJS tags:

<!DOCTYPE html>
<html>

<head lang="en">
    <meta charset="utf-8">

    <title>Historical Stock Quote Price</title>

    <style>
        thead { background: lightgray; align-content: center; }
        tbody { display: block; overflow: auto; height: 250px; border-collapse: collapse; }
        th { height: 40px; width: auto; border: 1px solid black; text-align: center; }
        td { height: 30px; width: 120px; margin: 0px; padding: 5px; border: 1px solid black;}
    </style>

</head>

<body>
    <table ng-app="StockHistoryApp" ng-controller="StockHistoryController">
        <thead>
            <tr>
                <th colspan="4">Historical Stock Quote Price</th>
            </tr>
        </thead>
        <tbody when-scroll-ends="loadMoreRecords()">
            <tr ng-repeat="stock in stockList">
                <td>{{stock.dateValue | date:'mediumDate'}}</td>
                <td>{{stock.price | currency:'$':2}}</td>
            </tr>
        </tbody>
    </table>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</body>

The table is being managed a StockHistoryController controller as part of a StockHistoryApp module. Each table record comes from the stockList collection in the
scope. Each row has a date and the corresponding stock price.

In order to have lazy loading in place, there has to be something to monitor the scrolling and trigger the load of more data. A custom AngularJS directive is an elegant approach, where a element can be enhanced in a declarative fashion. In our current example, that is the purpose of the when-scroll-ends directive in the tbody element

Now, the AngularJS code for the module:

    var appModule = angular.module('StockHistoryApp', []);

    appModule.constant('chunkSize', 50);

    appModule.controller('StockHistoryController', function ($scope, chunkSize) {
        $scope.stockList = [];

        var currentIndex = 0;
        var todayDate = new Date();
        $scope.loadMoreRecords = function () {
            // Mocking stock values
            // In an real application, data would be retrieved from an external system

            var stock;
            var i = 0;
            while (i < chunkSize) {
                  currentIndex++;
                  var newDate = new Date();
                  newDate.setDate(todayDate.getDate() - currentIndex);
                  if (newDate.getDay() >= 1 && newDate.getDay() <= 5) {
                    stock = {
                        dateValue: newDate,
                        price: 20.0 + Math.random() * 10
                    };
                    $scope.stockList.push(stock);
                    i++;
                }
            }
        };

        $scope.loadMoreRecords();
    });

    appModule.directive('whenScrollEnds', function () {
        return {
            restrict: "A",
            link: function (scope, element, attrs) {
                var processingScroll = false;

                var visibleHeight = element.height();
                var threshold = 100;

                element.scroll(function () {
                    var scrollableHeight = element.prop('scrollHeight');
                    var hiddenContentHeight = scrollableHeight - visibleHeight;

                    if (hiddenContentHeight - element.scrollTop() <= threshold) {
                        // Scroll is almost at the bottom. Loading more rows
                        scope.$apply(attrs.whenScrollEnds);
                    }
                });
            }
        };
    });

The loadMoreRecords function added to the scope is in charge of retrieving the stock data. For the sake of simplificity, the data is made-up randomly, rather than fetched from another system.

The custom directive

The directive takes advantage of JQuery to bind any scrolling of an element (in our case, the table body) to the execution of a callback (for our example, invokes the loadMoreRecords function) in case the scroll bar is close to the bottom.

Notice that the name directive added to the module is whenScrollEnds, a camel case without dashes name to match the when-scroll-ends in the HTML code, according to the normalization process performed by AngularJS.

In terms of directive properties, only two: restrict to specify that the directive needs to be declared as an element attribute (the “A” value) in HTML code, and link, given the need to manipulate DOM elements.

The second argument in the link function is a JQuery wrapper to the table body DOM element. By the way, if JQuery had not been loaded in the HTML code, the argument in the function would be a jqLite (that has a subset of JQuery API) wrapper instead.

The element.scroll(…) invocation is a shorthand available in JQuery API that would have the same effect of invoking element.on(‘scroll’, ….), namely the binding of a callback to the DOM onscroll event.

To verify whether the scroll bar is close to the bottom, what the callback does is checking the number of pixels scrolled down (the DOM scrollTop property) against the maximum number of pixels that can be scrolled, which is the difference between the entire table body height (the DOM scrollHeight property) and the visible table body height (the DOM height property). If there are less than 100 pixels (that is an arbitrary threshold) to the bottom, the loadMoreRecords is invoked. The attrs argument is a map of all attribute values inside the table body element and when-scroll-ends attribute is referable in AngularJS code through the same normalization process mentioned before.

References

Angular documentation – Creating custom directives

The JQuery API

HTML DOM Events

The HTML DOM Element Object