Aug 17, 2014

Writing Galen tests with Javascript

In version 1.0 a major change was introduced to execution and reporting system in Galen Framework. From now on it is possible to write and manage all your tests in Javascript. So what would be the difference compared to the original basic runner? What has changed is that you don’t need *.test files anymore. Instead you define your tests in javascript files with *.test.js suffix. Javascript gives you freedom to construct your own way of definining tests. Lets take a look at the simplest test:

test("Home page test", function () { // this is the body of the test // Instantiating the WebDriver, loading the page and changing the size of the browser window var driver = createDriver("", "1024x768"); // Checking layout on the page checkLayout(driver, "specs/homepage.spec", "desktop"); // Quiting the browser driver.quit(); });

If you want to parameterize test you can perform something like this:

var devices = { mobile: { size: "400x700", deviceName: "mobile" }, tablet: { size: "700x500", deviceName: "tablet" } }; forAll(devices, function () { test("Home page test on ${deviceName} on ${size} size", function (device) { // do something }); });

So that was the simplest way of implementing tests but you can check the documentation to see all the advanced stuff with Javascript test runner: Javascript Tests Guide

In this tutorial we will try to use most of those features in order to implement our own testing framework on top of Galen.
Lets use this application for our tutorial:

Writting our own framework

Initial Setup

First of all make sure you have the proper version. At the time of writting this article the latest galen version was 1.2.0
Lets create a proper directory structure. The project will be called galen-sample-tests. Inside our project we need to create two directories: specs and tests

galen-sample-tests/ |-- specs/ |-- tests/ |-- pages/

Now lets start writting our mini-framework in file tests/init.js

var domain = ""; /* A list of all devices that will be used in our tests */ var devices = { mobile: { deviceName: "mobile", size: "450x800", tags: ["mobile"] }, tablet: { deviceName: "tablet", size: "600x800", tags: ["tablet"] }, desktop: { deviceName: "desktop", size: "1100x800", tags: ["desktop"] } }; var TEST_USER = { username: "", password: "test123" }; /* This function creates an instance of WebDriver and stores it in a test session. Later it will be picked up by after-test event */ function openDriver(url, size) { var driver = createDriver(null, size); session.put("driver", driver); // Checking if url is actually a uri or a full url if (url != null) { if (url.indexOf("http://") != 0 && url.indexOf("https://") != 0) { url = "http://" + domain + url; } driver.get(url); } else { driver.get("http://" + domain); } return driver; } /* This event will be called after each test. Here we will close the browser window. Also in case of test failure we shall make a screenshot and attach it to the report */ afterTest(function (test) { var driver = session.get("driver"); if (driver != null) { if (test.isFailed()) {"Screenshot").withAttachment("Screenshot", takeScreenshot(driver)); } driver.quit(); } }); function _test(testNamePrefix, url, callback) { test(testNamePrefix + " on ${deviceName} device", function (device) { var driver = openDriver(url, device.size);, driver, device); }); } /* This function will be used in our tests in order to create a list of tests parameterize for each device that we have defined in the begining */ function testOnAllDevices(testNamePrefix, url, callback) { forAll(devices, function () { _test(testNamePrefix, url, callback); }); } /* This function is used in order to create a single test for a specific device */ function testOnDevice(device, testNamePrefix, url, callback) { forOnly({device: device}, function() { _test(testNamePrefix, url, callback); }); } /* Exporting functions to all other tests that will use this script */ (function (export) { export.devices = devices; export.openDriver = openDriver; export.testOnAllDevices = testOnAllDevices; export.TEST_USER = TEST_USER; })(this);

That looked a bit complex but now lets see how we can create a simple test. First of all we need a spec for a welcome page.
Once we have created it we can start writting tests. Create a file tests/welcomePage.test.js and put down the following:

load("init.js"); testOnAllDevices("Welcome page", "/", function (driver, device) { checkLayout(driver, "specs/welcomePage.spec", device.tags); });

The only problem that we have in this case is the fact that our web application is dynamic and takes a bit of time to render all of its elements. What we need to do is to properly wait for the page to be rendered. We can fix it by using the GalenPages - a mini-framework for functional testing.

Lets create a file in tests/pages/WelcomePage.js and put down the following:

importClass(org.openqa.selenium.interactions.Actions); this.WelcomePage = function (driver) { GalenPages.extendPage(this, driver, { loginButton: "#welcome-page .button-login" }); };

And now we can replace our test file tests/welcomePage.test.js with this:

load("init.js"); load("pages/WelcomePage.js"); testOnAllDevices("Welcome page", "/", function (driver, device) { new WelcomePage(driver).waitForIt(); checkLayout(driver, "specs/welcomePage.spec", device.tags); });

Thats it. Now it will properly wait for the page to be rendered by checking the availability of its primary controls. In our case we defined only one element “loginButton” but that is enough for now.


As you see using Javascript gives a lot of benefits to us as we can design tests in any way we want with minimal and readable code. If you want you can also checkout this Galen test project which is based on Javascript galen-sample-tests
In next articles I will introduce the technique for working with Feature Switches (Toggles) and also structured reporting for tests.

Posted in Automation, Galen, Selenium
comments powered by Disqus