I have built a simple Streamlit application and I would like to test the UI elements with the selenium
and pytest
framework. It is necessary to install a browser (e.g Google Chrome) and download its driver (in my case the ChromeDriver) before the UI test. After the download, I extracted the driver into a folder (e.g. <path_to_driver>/selenium) and added it to my path.
I would like to test the following Streamlit script names as app/Home.py
:
import streamlit as st
st.set_page_config(page_title="Home")
st.title("Welcome to My Streamlit App")
st.write("This is the home page of the application.")
st.header("Instructions")
st.write("Navigate to the sidebar to explore different pages:")
st.write("- Home: This page.")
st.write("- About: Learn more about the app.")
st.subheader("Features")
st.write("- Interactive data visualization.")
st.write("- Easy navigation between pages.")
st.info("Enjoy exploring the app!")
It sets the page title to "Home" and displays a title, introductory text, headers, and bullet-pointed features.
The below pytest
script tests the title of the Home page.
import subprocess
import time
import pytest
from selenium import webdriver
@pytest.fixture(scope="session")
def driver():
# Start the Streamlit application as a separate process
app_process = subprocess.Popen(
["streamlit", "run", "app/Home.py"]
)
# Wait for the Streamlit app to start
time.sleep(2)
driver = webdriver.Chrome()
yield driver
driver.quit()
# Stop the Streamlit application process after the test
app_process.terminate()
app_process.wait()
def test_home_title(driver):
driver.get("http://localhost:8501")
# How to find the streamlit element and then wait for them to being ready?
time.sleep(1)
assert driver.title == "Home"
This code snippet sets up a pytest fixture named driver
to test a Streamlit application using Selenium. The fixture runs once for all test cases (session scope) and performs the following steps:
subprocess.Popen
.Both time.sleep
can be avoided by using Selenium's built-in implicit or explicit waits functionality. However, I have no idea how to find the elements and wait for them to be ready.
How to find the elements (st.header
, st.write
, and st.info
) in the Streamlit application?
How to wait for them by using the Selenium built-in waiting functionalities?
There is a testing framework that eases the creation of the test cases, the seleniumbase. For Pythonista, it is an all-in-one testing framework. It uses Selenium/WebDriver APIs without downloading it explicitly and it incorporates pytest
, and behave
test-runners.
Once the testing framework is installed (pip install seleniumbase
), the test_home_title
test case will be reduced to:
import subprocess
from seleniumbase import BaseCase
class PageContentTest(BaseCase):
@classmethod
def setUpClass(cls) -> None:
cls.app_process = subprocess.Popen(["streamlit", "run", "app/Home.py"])
def test_home_page(self) -> None:
self.open("http://localhost:8501")
self.assert_title("Home")
# Assert the headers
self.assert_text("Welcome to My Streamlit App")
self.assert_text("Instructions")
self.assert_text("Features")
# Assert the info element
self.assert_element("div.stAlert")
@classmethod
def tearDownClass(cls) -> None:
cls.app_process.terminate()
cls.app_process.wait()
You do not see any webdriver construction and deconstruction. No need to define implicit or explicit waits because SeleniumBase waits under the hood. Inherit from the seleniumbase.BaseCase
and create methods for the test cases, the rest is automatically handled.
Execute the test with one of the supported test runners:
pytest --browser=chrome --headless
In this example, we test using the Chrome browser in a headless (without a graphical user interface) mode.
Note: Change the default browser by using the
--browser=firefox
option flag.