MDN wants to talk to developers like you: https://qsurvey.mozilla.com/s3/8d22564490d8

Introduction to automated testing

Manually running tests on several browsers and devices, several times per day, can get tedious and time consuming. To handle this efficiently, you should become familiar with automation tools. In this article we look at what is available, how to use task runners, and how to use the basics of commercial browser test automation apps such as Sauce Labs and Browser Stack.

Prerequisites: Familiarity with the core HTML, CSS, and JavaScript languages; an idea of the high level principles of cross browser testing.
Objective: To provide an understanding of what automated testing entails, how it can make your life easier, and how to make use of some of the commercial products that make things easier.

Automation makes things easy

Throughout this module we have detailed loads of different ways in which you can test your websites and apps, and explained the sort of scope your cross browser testing efforts should have in terms of what browsers to test, accessibility considerations, and more. Sounds like a lot of work, doesn't it?

We agree — testing all the things we've looked at in previous articles manually can be a real pain. Fortunately there are tools to help us automate some of this pain away. There are two main ways in which we can automate the tests we've been talking about in this module:

  1. Use a task runner such as Grunt or Gulp, or npm scripts to run tests and clean up code during your build process. This is a great way to perform tasks like linting and minifying code, adding in CSS prefixes or transpiling nascent JavaScript features for maximum cross-browser reach, and so on.
  2. Use a browser automation system like Selenium to run specific tests on installed browsers and return results, alerting you to failures in browsers as they crop up. Commercial cross-browser testing apps like Sauce Labs and Browser Stack are based on Selenium, but allow you to access their set up remotely using a simple interface, saving you the hassle of setting up your own testing system.

We will look at how to set up your own Selenium-based testing system in the next article. In this article we'll look at how to set up a task runner, and use the basic functionality of commercial systems like the ones mentioned above.

Note: the above two categories are not mutually exclusive. It is possible to set up a task runner to access a service like Sauce Labs via an API, run cross browser tests, and return results. We will look at this below as well.

Using a task runner to automate testing tools

As we said above, you can drastically speed up common tasks such as linting and minifying code by using a task runner to run everything you need to run automatically at a certain point in your build process. For example, this could be every time you save a file, or at some other point. Inside this section we'll look at how to automate task running with Node and Gulp, a beginner-friendly option.

Setting up Node and npm

Most such tools these days are based on Node.js (a server-side JavaScript environment that allows you to build server-side web applications, automate tasks, etc.), so you'll need to install it from nodejs.org:

  1. Download the installer for your system from the above site.
  2. Install it like you would any other program. Note that Node comes with Node Package Manager (npm), which allows you to easily install packages, share your own packages with others, and run useful scripts on your projects.
  3. One the install completes, test that node is installed by typing the following in the terminal, which returns the installed versions of Node and npm:
    node -v
    npm -v
  4. If you've got Node/npm already installed, you should update them to their latest versions. To update Node, the most reliable way is to download and install an updated installer package from their website (see link above). To update npm, use the following command in your terminal:
    npm install npm@latest -g

Note: If the above command fails with permissions errors, Fixing npm permissions should sort you out.

To start using node/npm-based packages on your projects, you need to set up your project directories as npm projects. This is easy to do.

For example, let's first create a test directory to allow us to play without fear of breaking anything.

  1. Create a new directory somewhere sensible with using your file manager UI, or by navigating to the location you want and running the following command:
    mkdir node-test
  2. To make this directory an npm project, you just need to go inside your test directory and initialize it, with the following:
    cd node-test
    npm init
  3. This second command will ask you a number of questions to find out the information required to set up the project; you can just select the defaults for now.
  4. Once all the questions have been asked, it will ask you if the information entered is OK. type yes and press Enter/Return and npm will generate a package.json file in your directory.

This file is basically a config file for the project. You can customize it later, but for now it'll look something like this:

{
  "name": "node-test",
  "version": "1.0.0",
  "description": "Test for npm projects",
  "main": "index.js",
  "scripts": {
    "test": "test"
  },
  "author": "Chris Mills",
  "license": "MIT"
}

With this, you are ready to move on.

Setting up Gulp automation

Let's look at setting up Gulp and using it to automate some testing tools.

  1. To begin with, create a test npm project using the procedure detailed at the bottom of the previous section.
  2. Next, you'll need some sample HTML, CSS and JavaScript content to test your system on — make copies of our sample index.html, main.js, and style.css files in a subfolder with the name src inside your project folder. You can try your own test content if you like, but bear in mind that such tools won't work on internal JS/CSS — you need external files.
  3. First of all, install gulp globally (meaning, it will be available across all projects) using the following command:
    npm install --global gulp-cli
  4. Next, run the following command inside your npm project directory root to set up gulp as a dependency of your project:
    npm install --save-dev gulp
  5. Now create a new file inside your project directory called gulpfile.js. This is the file that will run all our tasks. Inside the file, put the following:
    var gulp = require('gulp');
    
    gulp.task('default', function() {
      console.log('Gulp running');
    });
    This requires the gulp module we installed earlier, and then runs a basic task that does nothing except for printing a message to the terminal — this in itself is useful for letting us know that Gulp is working. Each gulp task is written in the same basic format — gulp's task() method is run, and given two parameters — the name of the task, and a callback function containing the actual code to run to complete the task.
  6. You can run your gulp task with the following commands — try this now:
    gulp
    

Adding some real tasks to Gulp

To add some real tasks to Gulp, we need to think about what we want to do. A reasonable set of basic functionality to run on our project is as follows:

  • html-tidy, css-lint, and js-hint to lint and report/fix common HTML/CSS/JS errors (see gulp-htmltidy, gulp-csslint, gulp-jshint).
  • Autoprefixer to scan our CSS and add vendor prefixes only where needed (see gulp-autoprefixer).
  • babel to transpile any new JavaScript syntax features to traditional syntax that works in older browsers (see gulp-babel).

See the links above for full instructions on the different gulp packages we are using.

To use each plugin, you need to first install it via npm, then require any dependencies at the top of the gulpfile.js file, then add your test(s) to the bottom of it, then finally add the name of your task inside the default task.

Before you go any further, update the default task to this:

gulp.task('default', [ ]);

Inside the array goes the names of all the tasks you want Gulp to run when you run the gulp command on the command line.

html-tidy

  1. Install using the following line:
    npm install --save-dev gulp-htmltidy
    

    Note: --save-dev adds the package as a dependency to your project. If you look in your project's package.json file, you'll see an entry for it has been added to the devDependencies property.

  2. Add the following dependencies to gulpfile.js:
    var htmltidy = require('gulp-htmltidy');
  3. Add the following test to the bottom of gulpfile.js:
    gulp.task('html', function() {
      return gulp.src('src/index.html')
            .pipe(htmltidy())
            .pipe(gulp.dest('build'));
    });
  4. Add 'html' as an item inside the array in the default task.

Here we are grabbing our development index.html file — gulp.src() allows us to grab a source file to do something with.

We next use the pipe() function to pass that source to another command to do something else with. We can chain as many of these together as we want. We first run htmltidy() on the source, which goes thorugh and fixes errors in our file. The second pipe() function writes the output HTML file to the build directory.

In the input version of the file, you may have noticed that we put an empty <p> element; htmltidy has removed this by the time the output file has been created.

Autoprefixer and css-lint

  1. Install using the following lines:
    npm install --save-dev gulp-autoprefixer
    npm install --save-dev gulp-csslint
  2. Add the following dependencies to gulpfile.js:
    var autoprefixer = require('gulp-autoprefixer');
    var csslint = require('gulp-csslint');
  3. Add the following test to the bottom of gulpfile.js:
    gulp.task('css', function() {
        return gulp.src('src/style.css')
            .pipe(csslint())
            .pipe(csslint.formatter('compact'))
            .pipe(autoprefixer({
                browsers: ['last 5 versions'],
                cascade: false
            }))
            .pipe(gulp.dest('build'));
    });
  4. Add 'css' as an item inside the array in the default task.

Here we grab our style.css file, run csslint on it (which outputs a list of any errors in your CSS to the terminal), then runs it through autoprefixer to add any prefixes needed to make nascent CSS features run in older browsers. At the end of the pipe chain, we output our modified prefixed CSS to the build directory. Note that this only works if csslint doesn't find any errors — try removing a curly brace from your CSS file and re-running gulp to see what output you get!

js-hint and babel

  1. Install using the following lines:
    npm install --save-dev gulp-babel babel-preset-es2015
    npm install jshint gulp-jshint --save-dev
    
  2. Add the following dependencies to gulpfile.js:
    var babel = require('gulp-babel');
    var jshint = require('gulp-jshint');
    
  3. Add the following test to the bottom of gulpfile.js:
    gulp.task('js', function() {
        return gulp.src('src/main.js')
            .pipe(jshint())
            .pipe(jshint.reporter('default'))
            .pipe(babel({
                presets: ['es2015']
            }))
            .pipe(gulp.dest('build'));
    });
  4. Add 'js' as an item inside the array in the default task.

Here we grab our main.js file, run jshint on it and output the results to the terminal using jshint.reporter; we then pass the file to babel, which converts it to old style syntax and outputs the result into the build directory. Our original code included a fat arrow function, which babel has modified into an old style function.

Further ideas

Once all this is all set up, you can run the gulp command inside your project directory, and you should get an output like this:

You can then try out the files output by your automated tasks by looking at them inside the build directory, and loading build/index.html in your web browser.

If you get errors, check that you've added all the dependencies and the tests as shown above; also try commenting out the HTML/CSS/JavaScript code sections and then rerunning gulp to see if you can isolate what the problem is.

Gulp comes with a watch() function that you can use to watch your files and run tests whenever you save a file. For example, try adding the following to the bottom of your gulpfile.js:

gulp.task('watch', function(){
  gulp.watch('src/*.html', ['html']);
  gulp.watch('src/*.css', ['css']);
  gulp.watch('src/*.js', ['js']);
});

Now try entering the gulp watch command into your terminal. Gulp will now watch your directory, and run the appropriate tasks whenever you save a change to an HTML, CSS, or JavaScript file.

Note: The * character is a wildcard character — here we're saying "run these tasks when any files of these types are saved. You could also use wildcards in your main tasks, for example gulp.src('src/*.css') would grab all your CSS files and then run piped tasks on them.

Note: One problem wth our watch command above is that our CSSLint/Autoprefixer combination throws full-blown errors when a CSS error is encountered, which stops the watch working. You'll have to restart the watch once a CSS error is encountered, or find another way to do this.

There's a lot more you can do with Gulp. The Gulp plugin directory has literally thousands of plugins to search through.

Other task runners

There are a number of other task runners available. We certainly aren't trying to say that Gulp is the best solution out there, but it works for us and it is fairly accessible to beginners. You could also try using other solutions:

  • Grunt works in a very similar way to Gulp, except that it relies on tasks specified in a config file, rather than written using JavaScript. See Getting started with Grunt for more details.
  • You can also run tasks directly using npm scripts located inside your package.json file, without needing to install any kind of extra task runner system. This works on the premise that things like Gulp plugins are basically wrappers around command line tools. So if you can work out how to run the tools using the command line, you can then run them using npm scripts. It is a bit more tricky to work with, but can be rewarding for those who are strong with their command line skills. Why npm scripts? provides a good introduction with a good deal of further information.

Using Sauce Labs to speed up browser testing

Test your code on Firefox for free on more than 800 browser/OS combos with our partner Sauce Labs.

There are other commercial browser testing systems available, but in this article we'll focus on Sauce Labs. We're not saying that this is necessarily the best tool available, but it is a good one that is smple for beginners to get up and running with.

The basic premise with such applications is that the company that runs each one has a huge server farm that can run many different tests. When you use the service, you provide a URL of a page you want to test, along with information such as what browsers you want it tested in. The app then configures a new VM with the OS and browser you specified, and returns the test results in the form of screenshots, videos, logfiles, text, etc.

You can then step up a gear, using an API to access functionality programmatically, which means that such apps can be combined with task runners, your own local Selenium environments, etc., to create automated tests.

Getting started with Sauce Labs

Let's get started with a Sauce Labs Trial.

  1. Create a Sauce Labs trial account.
  2. Sign in. This should happen automatically after you verify your e-mail address.

The basics: Manual tests

The Sauce Labs dashboard has a lot of options available on it. For now, make sure you are on the Manual Tests tab.

  1. Click Start a new manual session.
  2. In the next screen, type in the URL of a page you want to test (use http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box-fixed.html, for example), then choose a browser/OS combination you want to test by using the different buttons and lists. There is a lot of choice, as you'll see!
  3. When you click Start session, a loading screen will then appear, which spins up a virtual machine running the combination you chose.
  4. When loading has finished, you can then start to remotely test the web site running in the chosen browser.
  5. From here you can see the layout as it would look in the browser you are testing, move the mouse around and try clicking buttons, etc. The top menu allows you to:
    • Stop the session
    • Give someone else a URL so they can observe the test remotely.
    • Copy text/notes to a remote clipboard.
    • Take a screenshot.
    • Test in full screen mode.

Once you stop the session, you'll return to the Manual Tests tab, where you'll see an entry for each of the previous manual sessions you started. Clicking on one of these entries shows more data for the session. In here you can for example download any screenshots you took, watch a video of the session, and view data logs for the session.

Note: This is already very useful, and way more convenient than having to set all these emulators and virtual machines by yourself.

Advanced: The Sauce Labs API

Sauce Labs has a restful API that allows you to programmatically retreive details of your account and existing tests, and annotate tests with further details, such as their pass/fail state, which aren't recordable by manual testing alone. For example, you might want to run one of your own Selenium tests remotely using a Sauce Labs, to test a certain browser/OS combination, and then pass the test results back to Sauce Labs.

It has a number of clients available to allow you to make calls to the API using your favourite environment, be it PHP, Java, Node.js, etc.

Let's have a brief look at how we'd access the API using Node.js and node-saucelabs.

  1. First, set up a new npm project to test this out, as detailed in Setting up Node and npm. Use a different directory name than before, like sauce-test for example.
  2. Install the Node Sauce Labs wrapper using the following command:
    npm install saucelabs
  3. Create a new file inside your project root called call-sauce.js. give it the following contents:
    var SauceLabs = require('saucelabs');
    
    var myAccount = new SauceLabs({
      username: "your-sauce-username",
      password: "your-sauce-api-key"
    });
    
    myAccount.getAccountDetails(function (err, res) {
      console.log(res);
      myAccount.getServiceStatus(function (err, res) {
        // Status of the Sauce Labs services
        console.log(res);
        myAccount.getJobs(function (err, jobs) {
          // Get a list of all your jobs
          for (var k in jobs) {
            if ( jobs.hasOwnProperty( k )) {
              myAccount.showJob(jobs[k].id, function (err, res) {
                var str = res.id + ": Status: " + res.status;
                if (res.error) {
                  str += "\033[31m Error: " + res.error + " \033[0m";
                }
                console.log(str);
              });
            }
          }
        });
      });
    });
  4. You'll need to fill in your Sauce Labs username and API key in the indicated places. These can be retrieved from your User Settings page. Fill these in now.
  5. Make sure everything is saved, and run your file like so:
    node call-sauce

Advanced: Automated tests

We'll cover actually running automated Sauce Lab tests in the next article.

Summary

This was quite a ride, but I'm sure you can start to see the benefit in having automation tools do a lot of the heavy lifting for you in terms of testing.

In the next article we'll look at setting up our own local automation system using Selenium, and how to combine that with Sauce Labs.

Document Tags and Contributors

 Contributors to this page: chrisdavidmills, SpikePy, jpetto, billmcgee
 Last updated by: chrisdavidmills,