Automated testing with CucumberJS with Selenium Webdriver using JavaScript

I’m currently picking up something new at work. And I guess the title of the post gives it away. It’s not a brand new thing altogether, just something that I haven’t tried out before. And in one of the blogs I stumbled upon, there was a line that captured what I wanted to do in this post (and I guess in a lot of other previous posts too): helping me remember what I figure[d] out. Anyways, this is just a gist of the basic setup. Much of the learning is in the actual scripting, and most of it is when things don’t work as expected.

First things first: Setting up

  • Download and install Node from https://nodejs.org/en/download. This will also install the Node Package Manager (NPM).
  • Download the drivers into a folder in your local.
  • Update your environment variables to include the drivers into your PATH variable.
    • Add SELENIUM_DRIVERS as a system variable and specify the folder where you placed the drivers
    • Update the PATH system variable by appending “;%SELENIUM_DRIVERS%;”
  • We used a starter found here. Download the files from there, and that’ll give us stuff that I have no idea how to write from scratch at this point:
    • package.json
    • Gruntfile.js
    • features/support folder contains world.js, env.js and hooks.js
  • Install the needed dependencies.
    • Dependencies like CucumberJS, Chai, Selenium Webdriver, etc. are indicated in the package.json file.
    • Open the Command Prompt, then go to the folder where the starter files are placed. Run “npm install”.

Writing our Feature File and Step Definitions

Since we’re using CucumberJS, we’ll write up a feature file in Gherkin format. This means that scenarios that we’ll be testing for are written in a Given-When-Then format. Given includes the needed setup for the test. When refer to actions taken from the initial state (as setup in the Givens). And Then includes the expected outcomes that will be checked for.

For each Given, When and Then, corresponding code has to be written to actually perform the automated steps. These steps are stored in step definition files stored under features/step_definitions.

Feature: Searching for cucumbers
  As an internet user
  In order to find out more about cucumbers
  I want to be able to search for information about cucumbers

  Scenario: Google cucumber search
    Given I go to the Google website
    When I search Google for "cucumbers"
    Then I should see some results
'use strict';

var expect = require('chai').expect;

module.exports = function() {
  this.World = require('../support/world.js').World;

  this.Given('I go to the Google website', function (next) {
    this.browser.get('http://www.google.com').then(function () {
      next();
    });
  });

  this.When('I search Google for "$searchQuery"', function (searchQuery, next) {

    this.browser.findElement(this.webdriver.By.css('input[name="q"]')).sendKeys(searchQuery);
    this.browser.findElement(this.webdriver.By.css('input[name="q"]')).sendKeys(this.webdriver.Key.ENTER)
    .then(function () {
      next();
    });
  });

  this.Then('I should see some results', function (next) {
    this.waitFor('div.g', 10000);
    this.browser.findElements(this.webdriver.By.css('div.g')).then(function (elements) {
      expect(elements.length).to.not.equal(0);
      next();
    });
  });
};

Okay, actually writing our step definitions

To interact with the web site under test, we use Selenium Webdriver. While working on a POC, we had to look for elements within the page under test, and then interact with those elements by either triggering an action like clicking or sending keys or we also interact by extracting information about the element I found. For the checks in the Then steps, we use Chai to do the comparisons. For the POC at least, I pretty much managed to get by with simple scenarios using the items below.


//Finding Elements with Selenium Webdriver
//Find Element by ID
this.browser.findElement(this.webdriver.By.id('sp-search-input'))

//Find Element by CSS
this.browser.findElement(this.webdriver.By.css('#sp-search-input'))
this.browser.findElement(this.webdriver.By.css('.ui-chip'))
this.browser.findElement(this.webdriver.By.css('.ui-chip[data-val="hello"]'))

//Interacting with Elements with Selenium Webdriver
this.browser.findElement(this.webdriver.By.id('sp-search-input')).sendKeys(searchText+"\n");
this.browser.findElement(this.webdriver.By.id('sp-add-widget')).click();
this.browser.findElement(this.webdriver.By.css('.ui-chip>span')).getText();
//looks for element with class "ui-chip" and then looks for a <span> and gets the text in that <span>

this.browser.findElement(this.webdriver.By.id('sp-search-input')).getAttribute('placeholder');
//e.g., <input id="sp-search-input" placeholder="Add Search Tags" …>
//above will return "Add Search Tags"

this.browser.isElementPresent(this.webdriver.By.css('.ui-toggle.checked'));
//returns boolean value

//Comparisons using Chai
expect(actualText).to.equal(expectedText);
expect(textFound).to.contain(expectedString);
expect(toggleState).to.be.ok;

Running our scripts

Using the Command Prompt, go to your test project folder, then run “cucumber-js”. There’s also an option to run it with other options. I find the tags to be most helpful. What I do is I tag the scenarios that I’m working on in the feature file. The format of the tag is “@tagname” in the line before the scenario you are tagging.

cucumber-js --tags @dev               //tagged with @dev
cucumber-js --tags ~@dev              //NOT tagged with @dev
cucumber-js --tags @foo,@bar          //tagged with @foo OR bar
cucumber-js --tags @foo --tags @bar   //tagged with @foo AND bar

Well, that’s about it. Again, the learning really comes when you’re actually scripting. Sometimes, you just need to dive in knowing you don’t know everything, and hopefully you’ll figure it out as you go along.

Summary of references

Example, Starter – https://github.com/Matt-B/cucumber-js-selenium-webdriver-example
CSS Selectors – http://www.w3schools.com/cssref/css_selectors.asp
Chai – http://chaijs.com/api/bdd/
Selenium Webdriver – http://www.seleniumhq.org/docs/03_webdriver.jsp
CucumberJS – https://github.com/cucumber/cucumber-js
Gherkin – https://github.com/cucumber/cucumber/wiki/Gherkin
Cucumber – http://cukes.info
Cucumber tags – https://github.com/cucumber/cucumber/wiki/Tags
Blog post – http://www.tcias.co.uk/blog/2014/09/03/writing-our-first-functional-test/ (2014 Sep 3)
Blog post – http://transitioning.to/2012/01/cucumber-and-js-getting-started-with-cucumber-js/ (2012 Jan 27)

Advertisements

Selenium Simplified Notes 2 of X

Looking at the code in the previous notes, we can notice that there is one test (annotated with @Test).  That single test does just about everything:

  • It starts an instance of Selenium
  • It does the actions needed to conduct the search
  • It tests for 4 separate things (i.e., whether the result contains a particular text, whether the result contains a particular URL, it checks the page title, and it checks the content of the Search Box field)
  • It closes the browser and stops Selenium

Chapter 12 of the book focuses on refactoring our initial code. End result is that:

  • Separate methods were defined for the set up and tear down activities
  • Each test now only contains code that is relevant to the actual test
  • Initially the @Before and @After annotations were used — These methods will be done before and after each test
  • @BeforeClass and @AfterClass annotations were introduced. But this required some fields and methods to be changed to static so that they can be used across the entire class
package com.eviltester.seleniumtutorials;

import com.thoughtworks.selenium.*;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class MySimpleSearchTests {

	static Selenium selenium;

	@BeforeClass
	static public void automateTestSetup() {
		selenium = startSeleniumAndSearch();
	}

	@Test
	public void checkTextIsPresent() {
		assertTrue(selenium.isTextPresent("seleniumhq.org"));
		assertTrue("That long text is not found",
				selenium.isTextPresent(
				"Selenium Remote-Control Selenium RC comes in two parts." +
				" A server which automatically launches and kills browsers,"
				));
	}

	@Test
	public void checkUrlsAreFound() {
		int matchingCountTotal = 0;
		matchingCountTotal += selenium.getXpathCount("//a[@href='http://selenium-rc.seleniumhq.org']").intValue();
		matchingCountTotal += selenium.getXpathCount("//a[@href='http://selenium-rc.seleniumhq.org/']").intValue();
		// use starts-with instead
		matchingCountTotal += selenium.getXpathCount("//a[starts-with(@href, 'http://selenium-rc.seleniumhq.org')]").intValue();
		assertTrue("None of those URL were found", matchingCountTotal>0);
	}

	@Test
	public void checkPageTitle() {
		String pageTitle = selenium.getTitle();
		assertTrue("Search term not found in page title: " + pageTitle,
				pageTitle.contains("Selenium-RC"));
	}

	@Test
	public void checkSearchBox() {
		String searchTerm = selenium.getValue("xpath=//input[@name='q' and @title='Search']");
		assertTrue("Search term not found in search box, contains this instead: " + searchTerm,
				searchTerm.equals("Selenium-RC"));
	}

	@AfterClass
	static public void closeAndStopServer() {
		selenium.close();
		selenium.stop();
	}

	static private Selenium startSeleniumAndSearch() {
		Selenium selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://www.compendiumdev.co.uk/");
		selenium.start();
		selenium.open("/selenium/search.php");
		selenium.type("xpath=//input[@name='q']", "Selenium-RC");
		// When we use an XPath starting with '//', we can opt to leave out 'xpath='
		selenium.click("xpath=//input[@name='btnG' and @type='submit']");
		selenium.waitForPageToLoad("30000");
		return selenium;
	}

}

Selenium Simplified Notes 1 of X

So after checking out Alan Richardson’s free course over at udemy, I decided to check out his book.  I bought a digital copy of Selenium Simplified last month and started reading it. The downside of going with a book instead of his video trainings is I don’t get to hear his Irish accent. I try to read it with an accent (but only in my head) but it’s not really working out for me. Anyways, this is the part where I blog about my notes so that many months or years from now, in case I need to relearn what I’ve learned so far, I’ll just refer here instead of reading the entire book.

First things first – Setup

  1. Install Firefox (Although I highly suspect, Future Me will already have it installed.)
  2. Install Selenium IDE from Seleniumhq.org
  3. Install Java (Future Me, it might already be installed. Try java -version at the command line to check.)
  4. Install Selenium RC (well, it’s more of just copy the server .jar file and extract the client .zip files)
  5. Install Eclipse IDE for Java Developer
  6. When you create your Java project in Eclipse, you might need to configure your build path to add the server .jar and JUnit.

A simple java class

It’s not refactored. But it shows how to do a simple search and it has some simple assertions.

package com.eviltester.seleniumtutorials;

import com.thoughtworks.selenium.*;
import org.junit.Test;
import static org.junit.Assert.*;

public class MySimpleSearchTests {

@Test
public void searchAndCheckResults() {

Selenium selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://www.compendiumdev.co.uk/");
selenium.start();
selenium.open("/selenium/search.php");
selenium.type("xpath=//input[@name='q']", "Selenium-RC");
// When we use an XPath starting with '//', we can opt to leave out 'xpath='
selenium.click("xpath=//input[@name='btnG' and @type='submit']");
selenium.waitForPageToLoad("30000");

// Now here are the tests...
// Check if particular text is present
assertTrue(selenium.isTextPresent("seleniumhq.org"));
assertTrue("That long text is not found",
selenium.isTextPresent(
"Selenium Remote-Control Selenium RC comes in two parts." +
" A server which automatically launches and kills browsers,"
));

// Check if particular URLs are found
int matchingCountTotal = 0;
matchingCountTotal += selenium.getXpathCount("//a[@href='http://selenium-rc.seleniumhq.org']").intValue();
matchingCountTotal += selenium.getXpathCount("//a[@href='http://selenium-rc.seleniumhq.org/']").intValue();
// use starts-with instead
matchingCountTotal += selenium.getXpathCount("//a[starts-with(@href, 'http://selenium-rc.seleniumhq.org')]").intValue();
assertTrue("None of those URL were found", matchingCountTotal>0);

// Check if page title contains a particular text
String pageTitle = selenium.getTitle();
assertTrue("Search term not found in page title: " + pageTitle,
pageTitle.contains("Selenium-RC"));

// Check if the search box contains the search term
String searchTerm = selenium.getValue("xpath=//input[@name='q' and @title='Search']");
assertTrue("Search term not found in search box, contains this instead: " + searchTerm,
searchTerm.equals("Selenium-RC"));

selenium.close();
selenium.stop();

}

}

Start and stop the Selenium server

Don’t forget to start this up before running your test. Run this at the command line: java -jar selenium-server-standalone-2.33.0.jar To stop, just CTRL+C while at the command window. Or use this at your browser: http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer

Selenium discussed in pinoyjug meetup

Last Friday, a couple of friends and I headed over to the G2IX Techbar to attend a PinoyJUG meetup. It was the second time for my housemate and me, and I think it was a first for Renz. As with the last time, I had wanted to be as inconspicuous as possible. For one, I’m not a Java developer, and second, I’m not even a developer. Although I tend to rationalize (rather lamely) that it’s a Java User Group, and don’t we all use Java.

Anyway, there were two talks scheduled for that evening. One’s on Aspect Oriented Programming with Spring by Jasper Blues, and the other one which is more relevant to me was on Automated Testing with Selenium by Aaron Tinio.

I have tried playing around with the Selenium IDE before but not really in depth. I basically tried to create some tests on a login function using the record and playback feature. During the talk, the speaker demonstrated the use of Selenium RC which I’ve never tried out for myself or seen in action before. It was way better than the IDE since you can just issue out commands to manipulate and check your browser’s content. So that bit was really interesting for me. I also liked the faker gem he used to generate random data. That seems quite nifty.

The company’s intending to have some training lined up for the staff. I wonder if they (whoever is on the planning committee) are considering automated testing at all. Although I am not hopeful. But if they are, Selenium seems to be worth a shot.