Skip to main content

· 2 min read

We are pleased to announce that we now have a new mocking service for WebdriverIO called wdio-wiremock-service.

What can it do?

This service helps you to run WireMock seamlessly when running tests with WebdriverIO. It uses the well known Maven repository to download the WireMock jar for you which is then automatically installed, started and stopped. Stay up to date by joining the community over at our community Discord support server for help and support.

A few things you can do with this service:

  • Automatically run WireMock alongside the WebdriverIO testrunner
  • Supports usage of mock and fixture files
  • Match request URLs, methods, headers cookies and bodies using a wide variety of strategies. First class support for JSON and XML.
  • Configure WireMock with all the available options

Installation

npm install wdio-wiremock-service --save-dev

Instructions on how to install WebdriverIO can be found here.

Configuration

In order to use the service you need to add it to your service array:

wdio.conf.js
exports.config
// ...
services: ['wiremock'],
// ...
};

There are a lot more options that are available for configuration, for a full overview go here.

Creating mocks and fixtures

The service creates a directory (./mock by default) with two subdirectories (mappings and __files). It will use the mappings directory to look for mock files and it will use the __files directory to look for fixture files.

Knowing this, creating a fixture is as easy as creating a file in the __files directory:

./mock/__files/hello-world.json
Hello world

And creating your first mock is as simple as creating a file in the mappings directory:

Knowing this, creating a fixture is as easy as creating a file in the __files directory:

./mock/mappings/my-test.json
{
"request": {
"method": "GET",
"url": "/api/mytest"
},
"response": {
"status": 200,
"bodyFileName": "hello-world.json"
}
}

Writing tests

Writing your first test is really straight forward:

./test/specs/mytest.js
const fetch = require('node-fetch');
const assert = require('assert');

describe('My test', () => {
it('should assert the mock data', () => {
browser.call(async () => {
await fetch('http://localhost:8080/api/mytest')
.then((res) => res.text())
.then((body) => {
// assert that the request body returns the expected value
assert.equal(body, 'More content');
});
});
});
});

Support

Stay up to date by joining the community over our community Discord server to join others and for support and questions.

See you there!

· 2 min read

It's a common approach to filter specs with tags, grep or any other techniques, however we had a gotcha here before - a new session is created for every spec file which takes some time, especially for mobile tests.

We've added a feature that allows to filter spec files before a session is started. The feature is enabled by default for Cucumber framework only and is disabled by default for Mocha and Jasmine frameworks to avoid breaking changes. To use the feature, it has to be enabled in wdio.conf.js with feature flag, also all browser function calls like browser.addCommand() or any other have to be moved away from root scope. You can still use env flags, config or capabilities as before.

The following is only required for Mocha and Jasmine users who want to use the feature:

  • Enable the feature with a flag in wdio.conf.js
// wdio.conf.js
exports.config
// ...
featureFlags: {
specFiltering: true
},
}
  • Move custom commands declaration to before hook, if you have such, example:
// wdio.conf.js
exports.config
// ...
mochaOpts: {
/**
* all the files that interacts with `browser` object in a root scope
* have to be required in `before` hook if `specFiltering` feature is enabled.
*/
require: [
"@babel/register", // if you have any transpilers leave them as is
"./src/wdio/commands" // remove from here
]
},
before (capabilities, specs) {
require("./src/wdio/commands") // add here
},
}
  • Move custom command declarations from root scope to suite level (or move them to another file and require it in before hook, see 2.1), if you had such, example:
// my.spec.js

/**
* move `browser.addCommand()` as well as other browser functions calls
* from root scope to suite level (or another file)
*/
browser.addCommand('myCommand', () => {}) // remove!

// it's still possible to use config, capabilities or env flags as before.
describe('my suite in ' + browser.capabilities.browserName, () => {
// add it to suite/test scope
browser.addCommand('myCommand', () => {})

it('my test', () => {
browser.myCommand()
})
})

We are happy to answer any questions and awaiting your feedback.

Please note that the feature will be enabled for all test frameworks in v6 so it's recommended to start preparation in advance.

Thanks!

· 6 min read

Over the last few years a lot of new testing frameworks have evolved that provide different capabilities but also come with certain limitations. While tools like Selenium are often seen as slow and flaky and others like Cypress.io or Puppeteer are hyped as the future in the testing space. In reality all tools have their own use cases, have different levels of support and are based on a completely different architecture. Comparing them is rather useless as it ends up comparing apples with oranges. In larger test suites flakiness and race conditions can happen using all of these tools so that we can safely say that up to this point none of them (including WebdriverIO) has found the ultimate solution for world /(domin|autom)ation/.

At the end of the day there are more or less two different approaches to how you can automate a browser. One is using an official W3C web standard called WebDriver and the other is the native browser interface that some of the browsers expose these days. The WebDriver protocol is the de-facto standard automation technique that allows you to not only automate all desktop browsers but also run automation on arbitrary user agents that include mobile devices, desktop applications or even Smart TVs. If you don't use WebDriver your automation framework most likely uses the native browser interfaces to run its automation on. While in the past every browser had its own (often not documented) protocol, these days a lot of browsers, including Chrome, Edge and soon even Firefox, come with a somewhat unified interface that is close to what is called the Chrome DevTools Protocol. While WebDriver provides true cross-browser support and allows you to run tests on a large scale in the cloud using vendors like Sauce Labs, native browser interfaces often allow many more automation capabilities like listening and interacting with network or DOM events while often being limited to a single browser only.

With the release of WebdriverIO v5.13 we now introduce a new option that allows you to specify the automation protocol for your test and leverage the capabilities of both worlds. With that you can now decide whether to run your tests using WebDriver or Chrome DevTools (via Puppeteer). Nothing actually changes for your tests, just the automation happens using different technologies. We've created a new NPM package that we call devtools which has all WebDriver commands implemented but executes them using Puppeteer. It is a new type of plugin that even allows you to build your own WebDriver based automation package to automate an arbitrary device with WebdriverIO. The new option, called automationProtocol, expects a string and is by default set to "webdriver" to run automation using WebdriverIOs own webdriver bindings. However if you install the new devtools package via:

$ npm install --save-dev devtools webdriverio

and set automationProtocol: 'devtools' in your options, all the automation happens via Chrome DevTools (more specifically via Puppeteer) while using the same WebdriverIO command interface:

const { remote } = require('webdriverio')

let client;

(async function () {
client = await remote({
automationProtocol: 'devtools', // CHANGE PROTOCOL HERE
capabilities: { browserName: 'chrome' } // or `browserName: 'firefox'`
})

await client.url('https://webdriver.io')
console.log(await client.getTitle())
await client.deleteSession()
})().catch(async (e) => {
console.error(e.stack)
await client.deleteSession()
})

This is especially great for local testing since you are no longer required to download a browser driver which often causes confusion as to where to download and how to run them. In addition to that, tests will run much quicker since WebdriverIO is directly connected to the browser.

As mentioned before, you can now leverage the advantages of running true cross-browser tests at scale with WebDriver, as well as leveraging all the additional automation capabilities of DevTools in one single tool. That said, given the fact that it is not possible to switch the protocols during a session, you might want to have one set of tests that require Puppeteer involvement, while also having a second set that you run cross-browser in the cloud.

In order to access Puppeteer in your tests, we have introduced a command called getPuppeteer(), which returns the browser class of the Puppeteer framework. From there on you can access the Puppeteer interface and all of its other classes. Note that these interfaces provide commands that are solely promise-based and WebdriverIO does not wrap them as you might know it using the WDIO testrunner. In this case we recommend wrapping all Puppeteer command calls manually within the call command that ensures that all promises are resolved before it moves on with other commands. For example, the following script shows how you can use WebdriverIO using the devtools automation protocol via Puppeteer in the WDIO testrunner world:

describe('my e2e tests', () => {
// ...

it('replaces the WebdriverIO logo with the Puppeteer logo', () => {
browser.url('https://webdriver.io')

/**
* run Puppeteer code with promises to intercept network requests
* and replace the WebdriverIO logo in the docs with the Puppeteer logo
*/
browser.call(async () => {
const puppeteerBrowser = browser.getPuppeteer()
const page = (await puppeteerBrowser.pages())[0]
await page.setRequestInterception(true)
page.on('request', interceptedRequest => {
if (interceptedRequest.url().endsWith('webdriverio.png')) {
return interceptedRequest.continue({
url: 'https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png'
})
}

interceptedRequest.continue()
})
})

// continue with sync WebdriverIO commands
browser.refresh()
browser.pause(2000)
})

// ...
})

We also made sure that you can use the devtools automation protocol with WDIO testrunner services like @wdio/devtools-service to make the experience as seamless as possible. You can find the complete script example to run in standalone mode in the WebdriverIO example directory.

We recommend only running tests on the devtools protocol if your functional test requires some sort of automation capability that is not provided by WebDriver. It also makes sense to switch to devtools whenever running tests locally as the test execution will be much faster. If you follow our best practices you should split up your wdio config files per environment (e.g. local testing vs. run tests against Sauce Labs or in your grid). You can now have a config that defines a set of tests that require some Puppeteer interaction:

// wdio.devtools.conf.js
const { config } = require('./wdio.conf.js')
exports.config = Object.assign(config, {
/**
* set automation protocol to devtools
*/
automationProtocol: 'devtools',
/**
* define a set of tests that require Puppeteer involvement
*/
specs: [
'tests/e2e/devtools/**'
],
/**
* run tests only on Firefox and Chrome in headless mode
*/
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
headless: true
}
}, {
browserName: 'firefox',
'moz:firefoxOptions': {
headless: true
}
}]
// other specific options ...
})

With the new automationProtocol option, we've opened the project up to automation technologies beyond WebDriver. There are advantages and disadvantages using both approaches and with this feature we allow you to use both in one single tool. The devtools package is still work in progress and we are finalizing all of its features within the upcoming weeks. If you have any feedback, bugs or comments on this please reach out via Twitter or on our community Discord server.

Thanks!

Welcome! How can I help?

WebdriverIO AI Copilot