Skip to content
Home » Introduction to WebDriverIO Automation using Mocha Framework

Introduction to WebDriverIO Automation using Mocha Framework

  • by

WebdriverIO is a popular automating tool used for automating the web applications mostly developed on Node Js platform. Basically, it is JavaScript/Node.js-based test automation framework built on top of Selenium WebDriver. It allows developers and testers to automate browser interactions for testing web applications. WebdriverIO simplifies the process of writing and maintaining test scripts by providing a high-level API that abstracts away the complexity of Selenium WebDriver while offering modern features and integrations for JavaScript projects. WebdriverIO is also known as WDIO.

Key Features of WebdriverIO:

  1. Supports Modern Web Frameworks: WDIO supports frameworks like Mocha, Jasmine, and Cucumber, providing flexibility in writing test cases.
  2. Cross-Browser Testing: You can run tests on multiple browsers (Chrome, Firefox, Safari, etc.) and devices (mobile, desktop).
  3. Extensible: WebdriverIO has a robust plugin system, allowing integrations with reporting tools, assertion libraries, and more.
  4. Asynchronous Nature: With modern async/await syntax, WebdriverIO simplifies writing asynchronous code for interacting with browser elements.
  5. Supports Parallel Test Execution: WDIO allows parallel test execution across multiple browsers or devices, improving test efficiency.
  6. Cloud Service Integration: Integrates seamlessly with cloud-based services like BrowserStack and Sauce Labs for running tests on a wider range of devices and environments.

Guidelines or Steps to start WebdriverIO

The following are the steps to start with WDIO

1. Environment set up

Pre-requisites:

  • Node.Js – Firstly we need to make sure node.js application is installed in the machine. This can be downloaded from NodeJs offical website.
  • npm package – This comes with the node.js which is used for package management

Initialize the project:

  • Create a project directory
  • Initialize the node.js package (Run npm install in CLI)

2. Install WebDriverIO

  • Install wdio with the command
    – npm init wdio@latest ./
  • For writing and running script, we need text editors.
    ex: Visual studio code

3. Configuring WebdriverIO

exports.config = {
runner: 'local',
specs: ['./test/specs/*/.js'],
exclude: [],
maxInstances: 1,
capabilities: [{
maxInstances: 1,
browserName: 'chrome',
'goog:chromeOptions': {
args: ['--headless']
}
}],
logLevel: 'info',
bail: 0,
baseUrl: 'http://localhost',
waitforTimeout: 10000,
connectionRetryTimeout: 90000,
connectionRetryCount: 3,
services: ['selenium-standalone'],
framework: 'mocha',
reporters: ['spec'],
mochaOpts: {
ui: 'bdd',
timeout: 60000
}
};

4. Writing first test

Create a new file in spec folder

Start writing the test with the usage of mocha framework where the test are written inside describe and it block

ex:

describe('First test ',async ()=>
{
it('my first script',async()=>
{
await browser.url('https://www.google.com');
const title = await browser.getTitle();
expect(title).toBe('Google');
});
} );

Selectors

Selectors are generally used to identify elements and interact with the web elements present on a web page. WebdriverIO provides several types of selectors that can be used depending on how the web elements are structured. Understanding the selectors is key for writing effective automated test scripts. For querying the web elements we use $ and $$ symbol. We have different ways of identifying the web elements. When Query returns single element we use ‘$’ and when it returns multiple elements we use ‘$$

Following are the different type of Selectors available in WDIO:

1. By using css selectors

  • Class selector– This selector allows to select elements by their class name. We use ‘.’ before the class name
    Syntax: .classname
<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-testid="submit"
>
Submit
</button>
let submit= $('.btn')
  • ID selector Here we select the elements by their unqiue id attribute. we use # with id attribute value for identifying element associated with id selector.
    Syntax: #id
    ex: considering the above example
let submit= $('#main')

An anchor element with a specific text in it,can be identified using the query with the text starting with an equals (=) sign

ex: <a href="https://webdriver.io">WebdriverIO</a>
const link = await $('=WebdriverIO')


To find a anchor element whose visible text partially matches your search value, query it by using *= in front of the query string
ex: considering the above example

const link = await $('*=driver')

4. Element with certain text

In WDIO, we can perform a case-insensitive matching using .= or .*= within the query.

ex:<h1>Welcome to my Page</h1>
let header = await $('h1.=WeLcOme tO My PAge')
header = await $('h1.*=WeLcoMe')

5. Using Tag Name

For querying an element with specific tag name , we use  <tag> or <tag />
ex:

<my-element>Getting started with wdio</my-element>
const ele = await $('<my-element />')

6. Using Name Attribute


For querying elements with a specific ‘name’ we use [name=”some-name”]  as a selector parameter

ex: 
<input name="username" value="selenium" />

const elem = await $('[name="username"]')

7.Using Xpath

We can identify the web elements by its specfic xpath

html>
    <body>
        <p>foobar</p>
        <p>barfoo</p>
    </body>
</html>
const paragraph = await $('//body/p[2]')

8. Accessibility Name Selector

Accessibility Name Selector is nothing but querying the elements by their accessible name. Elements that are meant to be read by screen readers can be interacted within tests via their accessible names by using screen reading lables. The value of the accessible name can be both visual content or hidden text alternatives.

  • Fetch by aria-label:

For identifying the elements designed using aria-label. We use aria/the attribute value

<div aria-label="football">Hello World!</div>

const elem = await $('aria/football')
  • Fetch by aria- labelledby:

    We can find the webelement which use the attribute ‘aria-labelledby’ . We use aria/the text referring to the value used in aria-labelledby attribute
<button aria-labelledby="ref-1">Click Me!</button>
<div id="ref-1">Some Button</div>
const elem = await $('aria/Some Button')
  • Fetch by content

We can identify the webelement by its content like aria/content

<h1>Some Heading!</h1>

const elem = await $('aria/Some Heading!')
  • Fetch by title

Inorder to identify the elements with the title attribute, we use aria/value of title attribute

<a href="https://webdriver.io" title="foobar">Hello World!</h1>

const elem = await $('aria/foobar')
  • Fetch by alt property

We can identify the element with the alt attribute by using aria/value of alt attribute

<img src="/some/image.png" alt="Some Picture" />

const elem = await $('aria/Some Picture')
  • ARIA – Role Attribute

We can directly specify role of the element like [role= button] as selector parameter:

<button>Click me</button>

const button = await $('[role=button]')

5. Creating Page Object Pattern

By the evolution of the “elements as first class citizens” principle, it is now possible to build up large test suites using this pattern. Therefore it became easy to build up large test suites 

We need not include any additional packages for creating the page objects. Modern classes provide all necessary features we need:

  • inheritance between page objects
  • lazy loading of elements
  • encapsulation of methods and actions

The main objective of using page objects is to abstract any page information away from the actual tests. We need to store all the selectors and methods belonging to a particular page in the page object. Later, we can import the required class. Furthermore, if there occurs a redesiging of page, we can still run our test by modifying the page object file. Considering the internet herokuapp as example, below script shows the creation of page object and tests.

Making A Page Object

Define a class for each page of your application. This class will contain the selectors and methods that interact with the elements on the page. The test scripts will instantiate these page objects and call their methods to perform actions.

File name – page.js

export default class Page {
constructor() {
this.title = 'My Page'
}

async open (path) {
await browser.url(path)
}
}

Getting Selector

Firstly, write all important selectors and the required methods in our login.page.js object as getter functions:

// login.page.js
import Page from './page'
class LoginPage extends Page {
get username () { return $('#username') }
get password () { return $('#password') }
get submitBtn () { return $('form button[type="submit"]') }
get flash () { return $('#flash') }
get headerLinks () { return $$('#header a') }
async open () {
await super.open('login')
}
async submit () {
await this.submitBtn.click()
}
}

export default new LoginPage()

In Page Object pattern, we usually define getter methods to abstract and encapsulate web element selectors.These methods support lazy evaluation, meaning the system evaluates web elements only when needed. As a result, they help to create cleaner, more efficient, and maintainable code. We use getter methods in the Page Object pattern to define locators for web elements on a page. This approach allows the system to reference elements dynamically each time they are accessed without storing the web element as a static object

The following are the uses of having getter methods:

  • Lazy Evaluation: The Web Elements are called when we access the them , ensuring that we always interact with the updated version of the elements if the page is redesigned or updated
  • Readability : The test script looks cleaner, more readable, and easier to understand when we use getter methods to define the properties and their behavior. Getters encapsulate the details of how elements are located.
  • Ease of maintainance: Maintainability of scripts becomes easier when we use getter methods. Because when an element’s locator changes, we only need to update it once in the getter methods, and the changes will apply across the scripts, instead of updating it in multiple places

Usage of Page Objects In Tests

After defining the methods and selectors. We can start writing our test for the same. Inorder to use the page object we can use import and specify its path.

// login.spec.js
import LoginPage from '../pageobjects/login.page'

describe('login form', () => {
it('should deny access with wrong creds', async () => {
await LoginPage.open()
await LoginPage.username.setValue('foo')
await LoginPage.password.setValue('bar')
await LoginPage.submit()

await expect(LoginPage.flash).toHaveText('Your username is invalid!')
})

it('should allow access with correct creds', async () => {
await LoginPage.open()
await LoginPage.username.setValue('tomsmith')
await LoginPage.password.setValue('SuperSecretPassword!')
await LoginPage.submit()

await expect(SecurePage.flashAlert).toHaveText(
            expect.stringContaining('You logged into a secure area!'))
})
})

6. Execute the test

We can execute the test scripts in terminal with the below mentioned command. We can view the results in terminal with the help of spec reporter

npx wdio run wdio.conf.js

Data Driving and Parameterisation in WDIO:

In WebdriverIO, we can implement data-driven testing by storing the test data in a JSON file and looping through it in test scripts. This allows us to run the same test with different data sets, making your tests more modular and reusable.

Create a test data folder to store all the data that we require in tests. Create a JSON file with file extension ‘.JSON’ and add the data in that file. Each page object pattern will have data kept in each JSON file, so that the data can be individually driven to each test file. In order to user this data in our tests , we need to convert the JSON data to Object data. The following actions needs to be done in test file:

  • Import the ‘fs’ package
  • Convert the JSON data to String
    let data= fs.readFileSync(‘test/testdata/logindata.json’)
  • Convert the String data to object
    let testData= JSON.parse(data);
Example: //login.json
[
  {
    "username": "foo",
    "password": "bar"
  },
  {
    "username": "foo1",
    "password": "bar1"
  }
]

Using this data and parameterising in test file

import LoginPage from '../pageobjects/Login.Page.js'
import fs from 'fs'
let tdata= JSON.parse(fs.readFileSync('test/testdata/logindata.json'))
describe('Login Suite', async () => {
  tdata.forEach(({username,password}) => {
    it('Valid login credentials', async () => {
        await LoginPage.open();
       await LoginPage.login(username,password)
     });
    });
})

Conclusion

WebdriverIO (WDIO) is modern web automation testing especially in JavaScript environments. It supports for modern JS frameworks, flexible architecture, ease of integration, and robust selector options make it a highly effective tool for building maintainable and scalable test suites. Irrespective of the project size, that is whether it is a small projects or large, complex applications. Finally WDIO’s versatility, readability, and strong community support make it a preferred tool in the web automation space.