Run Tests in Parallel
Run Python tests in parallel to achieve faster builds
On BrowserStack, you can run multiple Selenium Webdriver tests at the same time across various browser, device and OS combinations. This is called Parallel Testing. Parallel Testing gives you the same benefits as running a multi-threaded application.
With Parallel Testing, you can run the same test on different browser/device combinations i.e. cross-browser testing, or run different tests on the same or different browser/device combinations. Parallel Testing will help you reduce the run time of your test suite, resulting in faster build times and faster releases.
You can start testing in parallel using one of the popular test frameworks which work with Python and Selenium or refer the following section. We have provided getting started guides on some of the popular frameworks below:
Run tests in parallel without a framework
You can achieve parallel testing in multiple ways even if you are not using one of the popular test frameworks which has feature for enabling parallel testing. We show three such ways below but this is not an exhaustive list:
- Using a multi-threaded program
- Using a multi-threaded program and using ThreadPoolExecutor to manage threads
- Using a shell script
Using a multi-threaded program
The following sample script shows a multi-threaded Python program. These are the salient points of the script:
- The same script is run across 5 different browser, device and OS combinations viz. Chrome on Win10, Edge on Win10, Safari on MacOS, and also on Android and iOS real mobile devices.
- The
Thread
function in thefor
loop takesrun_session
function and each set of capability from thecaps
array as an argument. - The
Thread
function then executes the same script on each set of capability as parallel sessions.
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
from selenium.webdriver.common.by import By
from threading import Thread
# This array 'caps' defines the capabilities browser, device and OS combinations where the test will run
caps=[{
'os_version': '10',
'os': 'Windows',
'browser': 'chrome',
'browser_version': 'latest',
'name': 'Parallel Test1', # test name
'build': 'BStack-[Python] Sample Build' # Your tests will be organized within this build
},
{
'os_version': '10',
'os': 'Windows',
'browser': 'Edge',
'browser_version': 'latest',
'name': 'Parallel Test2', # test name
'build': 'BStack-[Python] Sample Build'
},
{
'os_version': 'Big Sur',
'os': 'OS X',
'browser': 'Safari',
'browser_version': 'latest',
'name': 'Parallel Test3', # test name
'build': 'BStack-[Python] Sample Build'
},
{
'device': 'Samsung Galaxy S20',
'os_browser': '11.0',
'real_mobile': 'true',
'name': 'Parallel Test4',
'build': 'BStack-[Python] Sample Build'
},
{
'device': 'iPhone 12 Pro',
'os_browser': '14',
'real_mobile': 'true',
'name': 'Parallel Test5',
'build': 'BStack-[Python] Sample Build'
}]
#run_session function adds a product in cart bstackdemo.com
def run_session(desired_cap):
driver = webdriver.Remote(
command_executor='https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub',
desired_capabilities=desired_cap)
try:
driver.get("https://bstackdemo.com/")
WebDriverWait(driver, 5).until(EC.title_contains("StackDemo"))
# Get text of an product - iPhone 12
item_on_page = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/p'))).text
# Check if "Add to cart" button is present
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/div[4]'))).click()
# Check if the Cart pane is visible
WebDriverWait(driver,30).until(EC.visibility_of_element_located((By.CLASS_NAME, "float-cart__content")))
## Get text of product in cart
item_in_cart = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'))).text
# Verify whether the product (iPhone 12) is added to cart
if item_on_page == item_in_cart:
# Set the status of test as 'passed' or 'failed' based on the condition; if item is added to cart
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "iPhone 12 has been successfully added to the cart!"}}')
except NoSuchElementException:
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Some elements failed to load"}}')
# Stop the driver
driver.quit()
#The Thread function takes run_session function and each set of capability from the caps array as an argument to run each session in parallel
for cap in caps:
Thread(target=run_session, args=(cap,)).start()
Using a multi-threaded program and using ThreadPoolExecutor to manage threads
In case of the above implementation, the for
loop calls the run_session
function and all the threads are dispatched at the same time. You can also manage the dispatching by applying additional conditions on the loop but, in that case, you would have to manually keep track of which thread has completed its execution and depending on that, you will be required to spawn new threads keeping in mind you do not exceed the limit of parallel tests that is supported by your BrowserStack account.
You can choose to not manage the threads manually and use the Python ThreadPool
to do the same for you.
Suppose you have two parallel account and you have 20 tests to be run. You can pass the capability for 20 sessions in the caps
array and pass 2
as the value to max_workers
which represents the number of threads in threadpool. This will create two threads and execute the 20 sessions on each of the thread as and when each session completes execution.
from threading import Thread
from time import sleep
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
from concurrent.futures import ThreadPoolExecutor
from selenium.webdriver.common.by import By
#Your script will execute on each of the browser, device and OS combinations
caps=[{
'os_version': '10',
'os': 'Windows',
'browser': 'chrome',
'browser_version': 'latest',
'name': 'Parallel Test1', # test name
'build': 'BStack-[Python] Sample Build' # Your tests will be organized within this build
},
{
'os_version': '10',
'os': 'Windows',
'browser': 'Edge',
'browser_version': 'latest',
'name': 'Parallel Test2', # test name
'build': 'BStack-[Python] Sample Build'
},
{
'os_version': 'Big Sur',
'os': 'OS X',
'browser': 'Safari',
'browser_version': 'latest',
'name': 'Parallel Test3', # test name
'build': 'BStack-[Python] Sample Build'
},
{
'device': 'Samsung Galaxy S20',
'os_browser': '11.0',
'real_mobile': 'true',
'name': 'Parallel Test4',
'build': 'BStack-[Python] Sample Build'
},
{
'device': 'iPhone 12 Pro',
'os_browser': '14',
'real_mobile': 'true',
'name': 'Parallel Test5',
'build': 'BStack-[Python] Sample Build'
}]
#run_session function adds a product in cart bstackdemo.com
def run_session(desired_cap):
driver = webdriver.Remote(
command_executor='https://YOUR_USERNAME:YOUR_ACCESS_KEY@hub-cloud.browserstack.com/wd/hub',
desired_capabilities=desired_cap)
try:
driver.get("https://bstackdemo.com/")
WebDriverWait(driver, 10).until(EC.title_contains("StackDemo"))
# Get text of an product - iPhone 12
item_on_page = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/p'))).text
# Check if "Add to cart" button is present
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/div[4]'))).click()
# Check if the Cart pane is visible
WebDriverWait(driver,10).until(EC.visibility_of_element_located((By.CLASS_NAME, "float-cart__content")))
## Get text of product in cart
item_in_cart = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'))).text
# Verify whether the product (iPhone 12) is added to cart
if item_on_page == item_in_cart:
# Set the status of test as 'passed' or 'failed' based on the condition; if item is added to cart
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "iPhone 12 has been successfully added to the cart!"}}')
except NoSuchElementException:
driver.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Some elements failed to load"}}')
# Stop the driver
driver.quit()
#The `ThreadPoolExecutor` function takes `max_workers` as an argument which represents the number of threads in threadpool and execute multiple sessions on each of the thread as and when each session completes execution.
with ThreadPoolExecutor(max_workers=2) as executor:
executor.map(run_session, caps)
Using a shell script
If you have multiple test scripts in separate files that you want to execute in parallel, then you can do so using a simple shell script as shown below:
python <test-script-1>.py &
python <test-script-2>.py &
python <test-script-3>.py &
# and so on...
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!