In case you are looking to write a functional test in Javascript, the following tutorial provides a perfect structural and referential material for a UI automation engineer to Javascript testing with Selenium Webdriver 3, Mocha and NodeJS.
These days, Javascript is a ubiquitous web language which seems to overcome it’s ‘notorious’ past and has become a more solid platform not only for client, but for server domains. Mochajs, or simply Mocha, is a feature rich JavaScript test framework running on Nodejs which provides the platform and the API for building standalone applications in the server side using the Google’s V8 Javascript engine at its base.
*Note: to get started with this Javascript tutorial, you’ll need to be familiar with the basics of NodeJS and Javascript programming language.
Tutorial Overview:
1. Mocha Test Construction
- Introduction
- Installation
- Installing Chai Assertion Module
- Test suite and Test Case Structure
- Constructing Tests with Mocha
- Running Mocha’s Test Suite and Test Cases
- Managing Syncing of Async Testing Code
2. Using Javascript Selenium 3 API Integrated with MochaJS
- Selenium Introduction
- Selenium Installation
- Webdriver Construction
- Integrating MochaJS with Selenium Webdriver 3
Versions used:
- Node version used: 6.10.1 (LTS)
- Mocha: 2.5.3
- WebdriverJS: 3.3.0
1. Constructing Tests with Mocha
Introduction to Mocha
As mentioned, Mocha is a Javascript test framework which runs tests on NodeJS. Mocha comes in the form of a Node package via ‘npm’ allowing to use any library for assertions as a replacement to Node’s standard ‘assert’ function, such as ChaiJS. In addition, Mocha has several similar compounds with Jasmine, another popular test automation framework which we’ve mentioned in our research for Front End and Unit Test Automation Trends.
Mocha provides an API, which specifies a way to structure the testing code into test suites and test cases modules for execution and later on to produce a test report. Mocha provides two modes for running: either by command line (CLI) or programmatically (Mocha API).
Install Mocha
If mocha is to be used in CLI, then it should be installed globally as Nodejs.
npm install -g mocha
Install Chai Assertion Module
npm install --save chai
‘–save’ option is used in order for the module to be installed in project’s scope and not globally.
Test Suite and Test Case Structure
In Mocha, a test suite is defined by the ‘describe’ keyword which accepts a callback function. A test suite can contain child / inner test suites which can contain their own child test suites, etc. Whereas, a test case is denoted by the ‘it’ function which accepts a callback function and contains the testing code.
Mocha supports test suite setup and test case setup functions. A test suite setup is denoted by ‘before’ while a test case setup applies ‘beforeEach’. ‘beforeEach’ is actually a common setup for every case in the suite and will be executed before each case.
As with the setup, Mocha supports test suite and test case teardown functions. A test suite teardown is denoted by ‘after’, while test case teardown is implemented by ‘afterEach’ function which are executed after a test suite and after each test case, respectively.
Create a file that will ‘host’ the test suite, e.g. test_suite.js, and write to it the following;
describe("Inner Suite 1", function(){ before(function(){ // do something before test suite execution // no matter if there are failed cases }); after(function(){ // do something after test suite execution is finished // no matter if there are failed cases }); beforeEach(function(){ // do something before test case execution // no matter if there are failed cases }); afterEach(function(){ // do something after test case execution is finished // no matter if there are failed cases }); it("Test-1", function(){ // test Code // assertions }); it("Test-2", function(){ // test Code // assertions }); it("Test-3", function(){ // test Code // assertions }); });
Running Mocha Test Suite and Test Cases
Mocha supports execution of tests in three ways: Whole Test Suite file, tests filtered by “grep” patterns and tests grep filtering looking in a directory tree (recursive option)
- Run whole Test Suite file.
mocha /path/to/test_suite.js
- Run a specific suite or test from a specific suite file.
If a suite is selected then all the child suites and/or tests will be executed.
mocha -g “Test-2” /path/to/test_suite.js
- Run a specific suite or test file by searching recursively in a directory tree.
mocha --recursive -g “Test-2” /directory/
For extensive CLI options : mocha –help.
mocha –-help
Managing Syncing of Async Testing Code
In case async functions are used with Mocha and not handled properly, you may find yourself struggling. If asyncing code (e.g. http requests, files, selenium, etc.) is to be used in a test case, follow these guidelines to overcome unexpected results:
a) ‘done’ Function
In your test function (“it”) you need to pass the ‘done’ function down the callback chain, this ensures it is executed after your last step:
The example below emphasizes the done functionality, in this case 3 seconds of timeout will occur at the end of test function.
it(‘Test-1’, function(done){ setTimeout(function(){ console.log(“timeout!”); // mocha will wait for done to be called before exiting function. done(); }, 3000); });
b) Return Promise
Return a promise is another way to ensure Mocha has executed all code lines when async functions are used (‘done’ function is not needed in this case.)
it(‘Test-1’, function(done){ var promise; promise = new Promise(function(resolve, reject){ setTimeout(function(){ console.log("Timeout"); resolve(); }, 3000); }); // mocha will wait for the promise to be resolved before exiting return promise; });
2. Javascript Selenium 3 Integration with MochaJS:
Selenium Introduction
Selenium is a library that controls a web browser and emulates the user’s behavior. More specifically, Selenium offers specific language library APIs called ‘bindings’ for the user. These ‘bindings’ act as a client in order to perform requests to intermediate components and acting as servers in order to finally control a Browser.
Selenium APIs, or bindings, now exist in all popular developing languages. All language implementations have now agreed to keep a consistency with the API functions naming conventions.
The intermediate components could be the actual webdriver, found natively in each Selenium package: the selenium-standalone-server as well as vendor native browser controlling drivers, such as Geckodriver for Mozilla, chromedriver for Chrome, etc. Moreover, Selenium webdriver communicates with browser drivers via ‘JsonWired Protocol’ and becomes a W3C Web Standard.
Selenium Installation
Before diving any deeper into Selenium integration with MochaJS, we will take a quick look into Selenium implementation with NodeJS.
In order to use Selenium API for Javascript (or Selenium Javascript bindings), we should install the appropriate module:
npm install selenium-webdriver
At this point, it should be clarified that Javascript Selenium Webdriver can be equally referred (although not in npm,) to as “Webdriverjs”. Although, Webdrivejs is different than other libs/modules, such as WebdriverIO, Protractor, etc.: ‘selenium-webdriver’ is the official open source base Javascript Selenium library while the others are wrapper libs/frameworks that are built on top of webdriverjs API, claiming to enhance usability and maintenance.
In NodeJS code, the module is used by:
require(‘selenium-webdriver’)
Webdriver Construction
In order to be able to use Selenium, we should build the appropriate ‘webdriver’ object which will then control our browser. Below, we can see how we use the “Builder” pattern to construct a webdriver object by chaining several functions:
Builder with Options
var webdriver = require('selenium-webdriver') var chrome = require('selenium-webdriver/chrome'), var firefox = require('selenium-webdriver/firefox'); var driver = new webdriver.Builder() .forBrowser(‘firefox’) .setFirefoxOptions( /* … */) .setChromeOptions( /* … */) .build();
In the code above, we have managed to build a webdriver object which aggregates configuration for more than one browser (notice the ‘options’ methods), despite the fact that ‘forBrowser()’ method explicitly sets ‘firefox’.
The user can set the SELENIUM_BROWSER environmental variable on run-time to set the desired browser, it will override any option set by ‘forBrowser’, since we have already configured multiple browser capabilities by ‘set<browser_name>Options’.
The browser properties can have several types of information depending on the browser under test. For example, in Mozilla’s properties we can set the desired ‘profile’ configuration as follows:
var profile = new firefox.Profile( /* … path to firefox local profile … */); var firefoxOptions = new firefox Options().setProfile(profile);
Then, in the above Builder snippet we can add : ‘setFirefoxOptions( firefoxOptions )’
Builder with Capabilities
Selenium Webdriver Javascript API documents several ways that a webdriver could be build. One more possible way is by setting all the required driver configuration in capabilities:
var driver = new webdriver.Builder(). .withCapabilities( { ‘browserName’ : ‘firefox’ } ) .build();
Note that if setOptions are set after ‘withCapabilities’, the configurations will be overridden (e.g. Proxy configs).
Selenium Webdriver Control Flow and Promise Management
Since Javascript and NodeJS are based on asynchronous principles, Selenium Webdriver behaves in a similar way. In order to avoid callback pyramids and to assist a test engineer with the scripting experience as well as code readability and maintainability, Selenium Webdriver object incorporates a promise manager that uses a ‘ControlFlow’. ‘ControlFlow’ is a class responsible for the sequential execution of the asynchronous webdriver commands.
Practically, each command is executed on ‘driver’ object and a promise is returned. The next commands do not need to be nested in ‘thens’, unless there is a need to handle a promise resolved value as follows:
driver.get("http://www.google.com"); driver.getTitle().then(function( title ) { // google page title should be printed console.log(title) }); driver.quit();
Pointers for Javascript Testing with Selenium Webdriver and Mocha:
- ‘driver’ is a webdriver object not a promise object
- ‘driver.getTitle()’ or driver.get(url) or any other selenium command returns a promise object!
This means that we can perform the following:
var titlePromise = driver.getTitle(); titlePromise.then(function(title){ console.log(title); });
- Additionally, since ‘driver’ is asyncing in its base, the following will not work:
var title = driver.getTitle(); expect (title).equals("Google");
Note: ‘title’ is a promise object and not an actual resolved value.
MochaJS + Selenium Webdriver
Generally speaking, selenium webdriver can be integrated with MochaJS since it is used in any plain NodeJS script. However, since Mocha doesn’t know when an asynchronous function has finished before a ‘done()’ is called or a promise is returned, we have to be very careful with handling.
Promise Based
Selenium command registered automatically, to assure webdriver commands executed in the sequential and correct order a promise should be returned.
The codes bellow shows Mocha’s (before, beforeEach, after, afterEach) or test case body it hooks .
describe( 'Test Suite' , function(){ before(function(){ driver.get( my_service ); driver.findElement(webdriver.By.id(username)).sendKeys(my_username); // a promise is returned while ‘click’ action // is registered in ‘driver’ object return driver.findElement(webdriver.By.id(submit)).click(); }); after(function(){ return driver.quit(); }); it( 'Test Case', function(){ driver.getTitle().then(function(title){ expect(title).equals(my_title); })
The Following Actions Will Be Executed :
- Browser page of “my_service” is loaded
- Text Field with id ‘username’ is located
- Text Field with id ‘username’ is filled with ‘my_username’
- Page title is retrieved and checked for equality against ‘my_title’
- Webdriver quits and Browser window is closed and Browser process is terminated.
Selenium Webdriver Support for MochaJS
In order to perform Javascript testing with Selenium Webdriver and Mocha in a simple way, webdriver facilitates the usage with MochaJS by wrapping around MochaJS test functions (before, beforeEach, it, etc.) with ‘test’ object creating a scope that provide awareness that webdriver is being used. Therefore, there is no need for Promise returning.
First, the corresponding module should be loaded:
var test = require('selenium-webdriver/testing');
All the function of Mocha are preceded by ‘test.’ as follows:
test.before() test.describe(), etc
Then, the above code is fully re-written as:
test.describe( 'Test Suite' , function(){ test.before(function(){ driver.get( my_service ); driver.findElement(webdriver.By.id(username)).sendKeys(my_username); driver.findElement(webdriver.By.id(submit)).click(); }); test.after(function(){ driver.quit(); }); test.it( 'Test Case' , function(){ driver.getTitle().then(function(title){ expect(title).equals(my_title); }) driver.sleep(); }); });
Conclusion
In this tutorial we got a chance to experience Javascript testing with Selenium Webdriver and MochaJS. We should keep in mind the main difference when comparing to other programming language bindings due the asynchronous nature of NodeJS, MochaJS and Selenium Webdriver.
As long as we keep returning promises in any function which creates a promise (either a custom test lib function or a MochaJS hook/testcase), Mocha will execute them in the correct order.
Other frameworks such as WebdriverIO, Protractor and CodeseptJS, provide wrapper solutions that hide some configurations from the user and provide some Promise enhanced handling for better scripting experience that many test automation experts might find helpful.
-Please comment and share your experiences regarding Javascript testing with Selenium Webdriver and Mocha. What is your overall impression with these testing tools?
What is the advantage of using Mocha and NodeJS in Selenium Webdriver ?
JavaScript is using for both Protractor and Mocha, so what is the difference between Protractor and Mocha ?
Can you please explain more about this.
Hello Poovarj,
Protractor, Mocha and WebdriverJS are all NodeJS packages/modules.
Mocha and Protractor have completely different scopes.
WebdriverJS provides the base API that a tester should use for UI (e2e) automation.
Protractor is actually a wrapper library of WebdriverJS module which offers some rich features for enhanced usability such as more straightforward (less promises) commands syntax and usable handling of web elements created by Angular applications. So, protractor uses under the hood the core WebdriverJS library for UI (e2e) automation.
Mocha, on the contrary, is nothing more than a test runner library/framework, as Jasmine is. Mocha defines the structures of your tests, handling reporting, etc.
Protractor installation comes with Jasmine as the default test runner. Tester could then chose between Mocha or Jasmine.
Therefore, we could say that Protrator would be more usable compared to plain webdriverJS (as shown in this article) especially when Angular UI is tested.
I hope it helps 🙂
Thanks for explanation
Hello Dimos,
Thanks for providing the nice article regarding testing with Selenium Webdriver and Mocha.
But it will be really difficult as test grows and we include all tests (it()) in same suite file.
So, is there any way from which we can just refer to all the suite files from just one suite file and will have one suite file to run all the suites.
Regards
Hello Pankaj211289,
That’s an apparent problem as tests in a suite file grow in number and as well as test code gets more complex.
As in many unit-like frameworks (e.g. jUnit) it is suggested that Test Suite structure definition files are separated from Test Scripts definition. In this way test code is cleaner and more maintainable. Test code is … code after all.
So, a simple proposed solution would be the following example :
1. Have your “test_login_suite_file.js” file structured with ‘describe’, ‘it’, ‘before/after’, etc.
2. Have (one or more) “test_login_scripts.js” file in which you have define and implement your actual “it”, “before/after”, etc scripts which would actually be the callbacks passed to “it”, “before”, etc. This file should be sourced in your “test_login_suite.js” file (e.g. var loginScripts = require( “scripts/login/test_login_scripts.js” ) ).
In your test_login_suite_file.js you could write something similar to the following :
var loginScripts = require( “scripts/login/test_login_scripts.js”);
describe ( “Login suite”, function() {
before( loginScripts.setupLoginSuite );
it (“Check correct login” , loginScripts.checkCorrectLogin);
it (“Check wrong login” , loginScripts.checkWrongLogin);
});
The possibilities of the files structure can be tailored on your needs ..
Regards,
Dimos
Hello Dimos,
How to execute my test on browser already opened with selenium , i want to create diferent actions for instance 1-Init driver 2- Login 3-create reservation 4- delete reservation. Each steps(actions) should be run same session.
Thank you.
Hello Typhoon,
I assume that you have already created your script, either a plain NodeJS or based on MochaJS framework syntax.
You should issue your browser commands in a row on the same session by using the same created ‘driver’ object instance.
You should translate into webdriver commands what’s the “login”, “create reservation”, etc, business actions.
[dimos]