UI Testing with Nightwatch.js – Page Objects

I have already written a little about UI testing with Nightwatch.js. This was a little while ago and nightwatch.js has changed a little since then. In v0.7.0 they changed their implementation of page objects and added enhanced support. Nightwatch is a great framework for writing UI tests, and it easy to pick up an write some basic tests. But, if you are going to be writing lots of tests which you are more than likely going to, to ensure your UI is working as planned then you should really be making use of page object within the Nightwatch framework. Page objects are going to save you a lot of time, and prevent duplicated test code. By using page objects you able to abstract away a lot of the HTML actions needed when you are accessing and manipulating your pages. Also by using page objects you can increase your test coverage as you reuse the objects in multiple tests.

There are two parts to the page objects in the Nightwatch framework. Elements and Sections. For me, you need to be using both combined to get the most from the page objects. This is where you will get the most of them. To highlight this I’ve put together a couple of examples to explain how elements and sections work. Following on from my first post: UI testing with Nightwatch.js. I will be using the test from that post, and making two more test files, one that uses elements, and another that uses elements and sections together.

Getting started with page objects

Before you can get using page objects you need to update your config file to tell Nightwatch where to look for your page object files. In your Nightwatch config file you need to update the property:  page_objects_path. Mine looks like: 

"page_objects_path": "page-objects",

I am putting all my page objects inside of one folder so I use a single value in my config. Nightwatch allows you to set the value of page_objects_path as an array, with each element in the array a folder path. As your UI tests grow it might be worth looking at splitting them up into multiple folders for better maintainability. Each page object should be within it’s own file. Nightwatch will read the contents of each folder and these will become your page objects you can use within your tests.

Using Page Objects in your tests

Once you have updated your config to pull in the page objects you need to reference them inside of your tests to be able to call the commands or using the elements. You can name your files how ever you like, I name mine all one word using camel case if needed. You can use hyphens, underscores or even spaces in the file names if you so wish. Depending on how you name your files will matter, as you will need to reference the page object differently inside of your test files. All your page objects are available to all your test on the function argument .page context. I stick with the argument being named browser as per the Nightwatch documentation, but you can use whatever you wish.

The reason I keep the filename to camelCase is because the browser.page is a JavaScript object of all your page object files and folders. I have a page object file named simpleLogin.js which I reference in my tests as:

var login = browser.page.simpleLogin();

If you where to use hyphens in your file name you would need to use the following syntax.

var login = browser.page['simple-login']();

You can make your page object folder have nested folders. This would result in the browser.page object have a key for the folder name which is an object containing the page object files within that folder, you could then call that object like:

var login = browser.page.folder['simple-login']();

Page Elements and Sections

Page elements, or elements as they are referred to within the code are a way to reduce to keep the selectors you use in your tests DRY (Don’t repeat yourself). Rather than having to write the same selector over and over again in you test file you can add it as an element. An element is a property of a page object that have a selector attached to them. You can then use the element name in your test to reference to the selector of the HTML element you want to test for. This allows us to remove the duplicated selector references from our test files and move them into a single file. By doing this we only have one location to change if at a later point in time you are refactoring you application and change the class or ID of the element you are using in your test, you just need to update the selector value in your object file and all the test’s using that element will get the new value.

From the tests in my previous post, switching the selector I was using into page elements gives me the following elements

elements: {
  username: {
    selector: '#username'
  },
  password: {
    selector: '#password'
  },
  submit: {
    selector: 'input[type=submit]'
  },
  error: {
    selector: '.error'
  }
}

If you look at the Nightwatch documentation for page elements you will see there are a few different ways to set up you elements object. I go with the name of the element then an object containing selector and value. As you can see from my example we have four elements, username, password, submit, and error. Each have a selector value that corresponded to an HTML element within our page. You can split your elements down into sections if your object is dealing with a lot elements, you could split your elements object into sections to allow for better maintainability. See the Nightwatch documentation for more on using element sections.

To show you how you could use page elements in a test, here’s our initial test written without using page objects:

'Login Page Initial Render': function(browser) {
  browser
    .init()
    .waitForElementVisible( 'body', 1000 )
    .verify.visible('#username')
    .verify.visible('#password')
    .verify.value( 'input[type=submit]', 'Log In' )
    .verify.elementNotPresent('.error')
    .end()
}

Here’s the same test written using a page object that contains just page elements:

'Login Page Initial Render': function(browser) {
  var login = browser.page.simpleLogin();

  login.navigate()
    .waitForElementVisible( 'body', 1000 )
    .verify.visible('@username')
    .verify.visible('@password')
    .verify.value( '@submit', 'Log In' )
    .verify.elementNotPresent('@error')

  browser.end();
}

As you can see it’s not any shorter in length, possibly longer if anything. First off you need to setup the page object you wish to use in your test, you do this by setting a variable with the value being the page object. Then you start your test chain using this new variable, you can then make use of the elements from the page object by using the name of the element prefixed with an @, as you can see I’ve replaced #username with @username.

Page Commands

Page Commands, or commands called by the Nightwatch documentation is what makes using page objects all the worth while. By using commands you can make your test files really DRY, and move the bulk of your test logic into the page objects allowing future developers or tests who need to contribute to your test to reuse blocks of test’s without having to reinvent the wheel.

Commands are functions that contain logic for reuse in your tests, if you find yourself writing the same assertions or verify statements over and over, you should look at using a command. A really common example would be having to click submit on a form, rather then each test having the same code to click and verify something to do with the form submission you can wrap this up in a command and call the command inside of your test. Commands will still output to the terminal or your report for assertions and verify statements, there is no drastic difference between using a command verses having the test code within the test file. Using commands inside of your page objects make things more maintainable, and helps others who may need to write tests that touch parts of the page you have worked on.

If we take our first test, that’s not using pages objects and show you how we could write it using a page object that makes use of elements and commands

'Login Page Initial Render': function(browser) {
  browser
    .init()
    .waitForElementVisible( 'body', 1000 )
    .verify.visible('#username')
    .verify.visible('#password')
    .verify.value( 'input[type=submit]', 'Log In' )
    .verify.elementNotPresent('.error')
    .end()
}

Here’s the same test written to use page objects with elements and commands:

'Login Page Initial Render': function(browser) {
  var login = browser.page.commandsLogin();

  login.navigate()
    .validateForm()

  browser.end();
}

Sure makes our test a lot smaller, that’s because we have moved the logic into a page object command named “validateForm()”, which now contains the test logic for validating the form is present, inside of the page object we have the following

var loginCommands = {
  validateForm: function() {
    return this.waitForElementVisible('body', 1000)
      .verify.visible('@username')
      .verify.visible('@password')
      .verify.value('@submit', 'Log In')
      .verify.elementNotPresent('@error')
  }
};

module.exports = {
  commands: [loginCommands],
    url: function() { 
      return this.api.launchUrl; 
    },
  elements: {
    username: {
      selector: '#username'
    },
    password: {
      selector: '#password'
    },
    submit: {
      selector: 'input[type=submit]'
    },
    error: {
      selector: '.error'
    }
  }
};

As you can see, we have page elements and commands combined in our page object file for our login page. Overall a bit more code than our very first test, but this is a simple example, if you look at our other tests which is all on my GitHub, you will be able to see how I have used page objects that combine both elements and commands.

Take a look around the updated nightwatch-demo repository to see the other test’s and how I’ve switched them over to use page objects. You can even clone it or download the repository, I’ve updated it to contain all it’s required dependencies, and all runs from an npm install (tested on a Mac and Travis-CI only)

UI Testing with nightwatch.js

Update 26 December 2016: The code in the repo https://github.com/matthewroach/nightwatch-demo, has been updated to run from a clone/download. All dependencies are part of the repository or part of the npm install.

Nightwatch.js is an easy to use Node.js based End-to-End (E2E) testing solution for browser based apps and websites. It uses the powerful Selenium WebDriver API to perform commands and assertions on DOM elements.

Testing your website as a user may interact with it may not be your first thought when talking about testing code. We can unit test our code to ensure it’s doing as it’s supposed to but the more code we apply to our UI the more dependency it will have from the UI and the code powering it.

Most of the code and web apps I create are driven from a server side language with a front end interface, and then JavaScript applied on top to add a nice user experience.

With UI testing or integration testing we can test all the parts of the web application are working as intended. Ever upgraded a third party library or decided to adjust a little bit of code on a single page to later get a bug report from someone that another page in the app is now not working? YES, unit testing should of caught these issues, one of the added benefits from using a UI testing tool is you can be 100% sure that the integration from the code and tests you have written work at the final stage of delivery.

Installing nightwatch.js

If you have node and npm installed on your machine, it’s pretty simply to get nightwatch set up, you can install it local to your application or as a global module (-g). I have installed mine globally. Just run the following from your command line (you may need to do this as an admin or sudo on a Mac):

npm install nightwatch -g

Once you have it installed you can confirm you have it install by checking the version

nightwatch -v
nightwatch v0.6.8

Setting up

The code and test’s are available on Github if you wish to look: https://github.com/matthewroach/nightwatch-demo

When using nightwatch.js you will need a nightwatch.json file, this is the config file that contains a lot of different options, for this post I’ll not cover them all, just the one’s I’ve used, changed or added. I have this file located at the route of my project, you can place it anywhere under your project but you’ll need to update config items to point to the relevant locations of your project.

I am using browserstack’s automate service to run my tests with. You don’t have to use browser stack to run these test’s you can use selenium to run them locally. Using selenium takes a bit more set up to get running, so for the purpose of this post we will use browser stack.

Below is the nightwatch.json file I have located at the root of my project. The src_folders config points to my tests folder that’s also located on the root of the project. You can change this to point to anywhere you like.

You will also notice some references to hub.browserstack.com, this tell’s nightwatch to use browser stack as our selenium running. The two items you need to update to use this is the browserstack.user and browserstack.key. You will find these in your browser stack account.

{
  "src_folders": ["tests"],
  "output_folder": "reports",
  "custom_commands_path": "",
  "custom_assertions_path": "",
  "page_objects_path": "",
  "globals_path": "",

  "selenium": {
    "start_process": false,
    "server_path": "",
    "log_path": "",
    "host": "hub.browserstack.com",
    "port": 80,
    "cli_args": {
      "webdriver.chrome.driver": "",
      "webdriver.ie.driver": ""
    }
  },

  "test_settings": {
    "default": {
      "launch_url": "http://hub.browserstack.com",
      "selenium_port" : 80,
      "selenium_host" : "hub.browserstack.com",
      "silent": true,
      "screenshots": {
        "enabled": false,
        "path": ""
      },
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true,
        "browserstack.user": "",
        "browserstack.key": ""
      }
    }
  }
}

Tests

Test’s are written in JavaScript, I separate out my tests into a tests folder to keep separation from the app code. On bigger projects I’ve added extra folders with the tests folder to mimic the web app structure, so you are able to run selected areas on their own.

Each test file can have one or multiple test’s. Each test is a JavaScript function. A sample test is shown below:

module.exports = {
  'Login Page Initial Render': function( _browser ) {
    _browser
    .url('http://dev.matthewroach.me/login/')
    .waitForElementVisible( 'body', 1000 )
    .verify.visible('#username')
    .verify.visible('#password')
    .verify.value( 'input[type=submit]', 'Log In' )
    .verify.elementNotPresent('.error')
  }
}

The test shown above is very simple example that will open the URL: http://dev.matthewroach.me/login/ and check that the username and password fields are visible, the submit button has a value of Log In, and the error element is not visible.

One thing to note here is I am using the .verify object rather than the .assert object. The reason I prefer to use verify over assert is that the test’s will abort if you use assert, where as using verify it will record a fail on the test but carry on running through the other tests.

Running Tests

Now you have your first test and nightwatch all set up you can just call nightwatch from terminal in your project root, and you will see output like the following, you will noticed that it still outputs the test details even tho you are running test’s on browser stack. The output is also saved within browser stack, it looks slightly different in browser stack.

Nightwatch Output

When a test causes an error the output looks as follows

Nightwatch error output

 

Now you have the basics down, look at using page objects to make your tests DRY and reusable, see my post on UI Testing with nightwatch.js – Page Objects.