Make Selenium Tests Run Faster
Learn how to speed up your slow-performing Selenium tests.
Issue
Tests running on BrowserStack Automate slows down with the error as shown in the following image:
Cause
The absence of the keep-alive
parameter leads to slow-performing tests. When you don’t use this parameter, your client binding is using a new request every time you communicate with BrowserStack.
Why you must mitigate this issue
As an organization, optimizing your builds to run faster is crucial. BrowserStack understands the need to identify issues that might not be evident, but have a large impact on the speed and stability of your test sessions.
Use the resolutions provided to mitigate these issues so as to:
- Improve performance
- Reduce test flakiness
keep-alive
connection degrades your test performance approximately 2-10 times.
Resolution/Workaround
Since keep-alive
is an HTTP connection parameter, specifying it as a capability in the test script does not work. You need to specify this parameter at the Selenium Webdriver’s level so that the client binding uses the same connection for every request, which eventually speeds up your tests.
Use the following information to enable the keep-alive
parameter in your test script:
In your test script, when initializing the driver, use the keep_alive
parameter and set it to True
, as shown in the following code snippet.:
driver = webdriver.Remote(
command_executor='https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub',
desired_capabilities=desired_cap, keep_alive=True)
Sample test script
Here is a sample test script that uses the keep_alive
parameter to perform a test case:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
desired_cap = {
'browserName': 'iPhone',
'device': 'iPhone 12 Pro Max',
'realMobile': 'true',
'os_version': '14',
'name': 'BStack-[Python] Sample Test', # test name
'build': 'BStack Sample Build' # CI/CD job or build name
}
driver = webdriver.Remote(
command_executor='https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub',
desired_capabilities=desired_cap, keep_alive=True)
driver.get("https://bstackdemo.com/")
try:
WebDriverWait(driver, 5).until(EC.title_contains("StackDemo"))
# Setting the status of test as 'passed' or 'failed' based on the condition; if title of the web page starts with 'StackDemo'
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Title contains StackDemo!"}}')
except TimeoutException:
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Title does not contain StackDemo."}}')
driver.quit()
In your Node.js project or any other Node.js based framework’s project such as WebdriverIO, NightwatchJS, etc., do the following:
Step 1: Create a file named fast-selenium.js
in the folder where your test script file is located.
Step 2: Add the following code to the fast-selenium.js
file
var http = require('http'),
https = require('https');
//set the time (in seconds) for connection to be alive
var keepAliveTimeout = 30*1000;
if(http.globalAgent && http.globalAgent.hasOwnProperty('keepAlive')) {
http.globalAgent.keepAlive = true;
https.globalAgent.keepAlive = true;
http.globalAgent.keepAliveMsecs = keepAliveTimeout;
https.globalAgent.keepAliveMsecs = keepAliveTimeout;
} else {
var agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: keepAliveTimeout
});
var secureAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: keepAliveTimeout
});
var httpRequest = http.request;
var httpsRequest = https.request;
http.request = function(options, callback){
if(options.protocol == "https:"){
options["agent"] = secureAgent;
return httpsRequest(options, callback);
}
else {
options["agent"] = agent;
return httpRequest(options, callback);
}
};
}
Step 3: Edit your existing test script to add the following require
statement that imports the fast-selenium.js
file:
require('./fast-selenium.js');
Sample test script
Here is a sample test script that uses the fast-selenium.js
file to perform a test case:
require('./fast-selenium.js'); // imports the fast-selenium script
const webdriver = require('selenium-webdriver');
// Input capabilities
const capabilities = {
'device' : 'Samsung Galaxy S21 Plus',
'realMobile' : 'true',
'os_version' : '11.0',
'browserName' : 'Android',
'name': 'BStack-[NodeJS] Sample Test', // test name
'build': 'BStack Sample Build' // CI/CD job or build name
}
async function runTestWithCaps () {
let driver = new webdriver.Builder()
.usingServer('https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub')
.withCapabilities(capabilities)
.build();
await driver.get("https://bstackdemo.com/");
try {
await driver.wait(webdriver.until.titleMatches(/StackDemo/i), 5000);
await driver.executeScript(
'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed","reason": "Title matches to StackDemo!"}}'
);
} catch (e) {
await driver.executeScript(
'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed","reason": "Title does not match to StackDemo"}}'
);
}
await driver.quit();
}
runTestWithCaps();
Step 1: In your Ruby project, create a file named fast-selenium.rb
in the folder where you test script file is located.
Step 2: Add the following code to the fast-selenium.rb
file:
require 'selenium/webdriver/remote/http/curb'
module Selenium
module WebDriver
module Remote
class Bridge
attr_accessor :http_curb
def http
unless @http_curb
@http_curb = Http::Curb.new
@http_curb.server_url = @http.send(:server_url)
end
@http_curb
end
end # Bridge
end # Remote
end # WebDriver
end # Selenium
Step 3: Edit your existing test script to add the following require
statement that imports the fast-selenium.rb
file:
require './fast-selenium'
Sample test script
Here is a sample test script that uses the fast-selenium.rb
file to perform a test case:
require 'rubygems'
require 'selenium-webdriver'
require './fast-selenium'
# Input capabilities
caps = Selenium::WebDriver::Remote::Capabilities.new
caps['device'] = 'iPhone 12 Pro Max'
caps['realMobile'] = 'true'
caps['os_version'] = '14'
caps['javascriptEnabled'] = 'true'
caps['name'] = 'BStack-[Ruby] Sample Test' # test name
caps['build'] = 'BStack Sample Build' # CI/CD job or build name
driver = Selenium::WebDriver.for(:remote,
:url => "https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub",
:desired_capabilities => caps)
# Opening demo website
driver.navigate.to "https://bstackdemo.com/"
wait = Selenium::WebDriver::Wait.new(:timeout => 5) # seconds
begin
wait.until { !driver.title.match(/StackDemo/i).nil? }
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Title matches to StackDemo!"}}')
rescue
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Title does not matches to StackDemo"}}')
end
driver.quit
The keep-alive
parameter is enabled by default on most client binding versions of Java and C#. If you’re using one of these languages and still encounter the error, update your client binding to the latest version.
Contact us if you encounter the error even after updating the client binding version.
Verify resolution of error
Use the information in the following tabs to verify whether this error is resolved.
- Go to your Automate dashboard, navigate to your session, and click the
Issues Detected
tab. - Check if the
HTTP keep-alive missing
error appears on the tab. Assume the error as resolved if no error is seen.
- Go to your Automate dashboard, navigate to your session, and click the
Other Logs
tab. - Click
View Raw Selenium Logs
. A new tab opens that displays the Selenium logs of your test session. - Search for
x-connid
in the logs and check whether the value ofx-connid
is the same for every request.
If the value of every instance of the x-connid
parameter is the same then the error has been resolved. Same value of x-connid
denotes that your client binding is using the same connection for every request.
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
Thank you for your valuable feedback!