Selenium 4 introduces a new powerful API which grants access to Chrome DevTools directly from your automated tests! This is done via the Chrome DevTools Protocol (or CDP for short) which is essentially a set of tools that enables you to access and control Chromium-based browsers.
You may know Chrome DevTools as the place where you inspect elements to find their locators. But there are lots of other domains here as well such as Network, Performance, and Security. There’s also a bunch of commands and sensors available.
Selenium 4’s new Chromium DevTools API is essentially a wrapper around the raw CDP commands, and access to these commands from our tests opens a world of possibilities!
For example, we can mock the geolocation of our browser to do testing in areas where we aren’t actually physically located. We can simulate things like network speed and device mode. We can intercept network requests and mock responses. And we can capture console logs and performance metrics. Essentially anything that can be done from the Chrome DevTools window can now be done from our automated tests!
Shama Ugale has provided Selenium 4 CDP examples complete with code snippets! And in this post, I’ll provide the architecture to help you better understand how to use CDP in your tests.
ChromiumDriver
ChromiumDriver is a new driver that can be used to interact with Chromium-based browsers such as Google Chrome and Microsoft Edge. The browser-specific drivers ChromeDriver and EdgeDriver still exist in Selenium 4, but now they inherit from ChromiumDriver instead of the RemoteWebDriver class (which ChromiumDriver now extends).
From ChromiumDriver (or any of its children classes), you can now access the getDevTools() method which returns a DevTools object.
DevTools devTools = driver.getDevTools();
DevTools Object
The DevTools object has methods to manage CDP sessions, add and remove listeners, and most notably send CDP commands. The send() method accepts a Command, which are the Selenium-provided wrappers for CDP commands.
The wrapper classes represent the 45 CDP Domains and can all be found in the org.openqa.selenium.devtools.vXX package, where the vXX represents the browser version you’re running against.
For example, I’m currently using Chrome 88, so I must ensure I have the corresponding devtools dependency:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-devtools-v88</artifactId>
<version>4.0.0-beta-1</version>
</dependency>
Here are the CDP Domains. Each of these have a corresponding wrapper class in Selenium 4.
Accessibility
Animation
ApplicationCache
Audits
BackgroundService
Browser
CacheStorage
Cast
Console
CSS
Database
Debugger
DeviceOrientation
DOM
DomDebugger
By clicking on any of the CDP Domain links above, you’ll see the available commands. Each of these commands are methods within the Selenium domain wrapper classes. This makes it relatively straightforward to invoke a CDP command using the send() method.
For example, the signature for the send() method is
devTools.send(Command<X> command)
And if we wanted to invoke a command, we could do so by passing in the {Domain}.{command} to the send() method. Let’s say we wanted to clear the browser cache. The Network domain has a clearBrowserCache command, so this is what we’d use in Selenium as well.
devTools.send(Network.clearBrowserCache());
executeCdpCommand
Selenium 4 also provides the executeCdpCommand() method. I’m already seeing people jump straight to this method as opposed to the send() method, as the name is very enticing. The executeCdpCommand() method is available directly from ChromiumDriver (and its descendent classes). The purpose of this method is to allow you to invoke any raw CDP commands. As the devtools package has all of the ready-made wrappers available, the send() method should be your first choice to invoke CDP commands. The executeCdpCommand() method should be used when you want to bypass Selenium’s implementation and call the CDP commands directly. The use of executeCdpCommand() requires a bit more work, as you’ll need to paste in the exact Domain.command as well as a Map of all of the command’s parameters.
For example, here’s a call to emulate a geolocation using executeCdpCommand(). Notice, I need to manually type in the names for the Domain, command, parameters as well as the parameter values.
driver.executeCdpCommand(
"Emulation.setGeolocationOverride",
Map.of(
"latitude", 30.3079823,
"longitude", -97.893803,
"accuracy", 1
));
And here’s the more streamlined way of invoking that same CDP command by using the DevTools API that Selenium provides. Here I only need to manually type in the values. Selenium takes care of everything else, so there’s less room for error.
devTools.send(Emulation.setGeolocationOverride(
Optional.of(35.8235),
Optional.of(-78.8256),
Optional.of(100)));
Hopefully this helps you understand CDP better and how you may be able to take your tests to the next level!