OVERVIEW
Unit testing is essential for early bug detection and fixing. Failure to carry it out can result in bug accumulation, increasing bug-fixing time and cost as the application scales. So it’s a crucial part of the software development journey you don’t want to ignore.
It is a known fact that the cost and severity of the bugs multiply when they are detected at later stages of the project. This is where unit testing becomes extremely important, as it ensures that the code works well at the unit level. For starters, unit testing comes before integration and system testing.
Each one of us would have written some or the other form of unit test in their development (or QA) careers :) And it lets you ascertain each function or unit of your program separately so you can mitigate associated bugs.
Python for automation testing is a preferred choice since it supports a wide range of test automation frameworks, facilitates dynamic typing, and many more features that are helpful for developers and testers alike. In this Python unit testing tutorial, we’ll critically look into Python unit testing using the unittest framework.
So, let’s get started with this Python unit testing tutorial!
Unittest is Python’s built-in, open-source unit testing framework. However, it also acts as a test runner for high-level testing, including integration, system, and performance testing.
Unittest is versatile and works with browser-based UI testing frameworks like Selenium to run automated tests on cloud grids.
The framework is easy to use, offering many straightforward test assertion methods for easy code validation. All you have to do is import the unittest module into your test script and invoke its TestCase class as an inherited object in your test class.
Here’s an example of how to inherit the unittest TestCase class in your test class while performing unittest testing:
import unittest
class testCaseExample(unittest.TestCase):
def methods(self):
actions…
Unit testing validates each function, class, or unit of a program or code separately in isolation to check if they work as intended. It tests each unit independently without considering its relationship with others.
Ideally, unit testing helps the development team achieve the following:
Delve into our top Unit Testing Interview Questions guide, designed to help you excel in unit testing interviews. It covers a wide range of topics, from syntax to advanced techniques, with detailed solutions.
Many people define unit and component testing interchangeably. But they’re not the same. Let’s see how they differ.
As mentioned, unit testing tests each function or module at the unit level. It isolates each of these modules or functions to test them separately.
During unit testing, you need a working knowledge of the underlying code and how it works. Additionally, the developers are the ones who carry out unit testing.
Component testing tests each software feature separately in isolation. It usually comes before integration testing and doesn’t consider the relationship between each component.
Unlike unit testing, component testing uses the black box testing principle. Additionally, testers carry out component testing. And they don’t need background knowledge of the code running the component.
Run your Python automated scripts on 3000+ browser environments. Try LambdaTest Now!
Unit testing is an essential part of software development, and it is crucial to follow best practices to ensure that tests are reliable, efficient, and effective. Here are some unit testing best practices you can follow:
Mocking is a unit test technique that ignores external dependencies while testing. It helps your test focus on the code under test without the influence of external dependencies. This is a worthy step since external modules can result in test failure or bias.
You can mock external dependencies in Python by including the MagicMock method from unittest.mock in your unit test case. This unittest method allows you to stub external dependencies, preventing their influence on your test.
The Python unit testing environment setup is easy. We will run our test cases on the Windows platform using Python 3 in this Python unit testing tutorial, but you can catch the details if you’re using other platforms.
First, Python must be running on your machine. Besides this, you only need to install Selenium into your virtual or global environment, start a new test project, and you’re all set.
First, Python must be running on your machine. Besides this, you only need to install Selenium into your virtual or global environment, start a new test project, and you’re all set.
To install Python on Windows:
Next is the creation of your project parent folder; this is where your test scripts reside.
You can create your project folder in any way that works best for you and open it to any IDE you choose.
But if you’re unfamiliar with project folder creation, we’ve described the process below using VS Code, our chosen IDE, for this Python unit testing tutorial.
To create a project folder in VS Code on Windows:
You can run your project globally if you like. But to avoid dependency interference with the global modules, you want to create a new virtual environment for your test project. This isolates your development environment.
Python has a pre-installed virtual environment manager, virtualenv. So you don’t need to install this separately.
To create a virtual environment on Windows, navigate to your project root folder from the command line. Then run the following command, replacing virtual_env_name with your preferred name:
virtualenv virtual_env_name
Next, cd into the scripts folder of the virtual environment you just created:
cd virtual_env_name/scripts
Activate the virtual environment from here by running the activate command. The virtual environment name should now be in parenthesis (for Windows):
activate
Finally, cd back into your project root folder by running the following command twice consecutively:
cd ..
Here’s the entire command-line activity:
As mentioned earlier, you don’t need to install unittest separately since it’s a pre-installed Python unit testing framework.
However, you must install Selenium since we’re running a front-end test. You also want to install the dotenv package to retrieve masked sensitive information from your machine’s environment variables. We’ll also add the HTML test runner package, as we’ll use this to retrieve and save test results locally.
To install these packages, create a requirements.txt file in your project root folder and list each dependency as shown:
FileName - project_folder/requirements.txt
selenium
python-dotenv
html-testrunner
Navigate to your project root directory from your command line and install the dependencies from requirements.txt:
pip install -r requirements.txt
The installation process runs as shown:
In this section of the Selenium Python testing tutorial, we’ll demonstrate all test cases on the LambdaTest cloud grid. LambdaTest is a reliable and scalable continuous quality platform that operates on a cloud-based infrastructure for Selenium Grid. This platform empowers you to perform Python automation testing on more than 3000 real browsers and operating systems. Moreover, it features parallel test execution that can notably shorten your build times during Python web automation.
You can also Subscribe to the LambdaTest YouTube Channel and stay updated with the latest tutorials around, Selenium testing, Playwright, Appium, CI/CD, and more.
So our unit test demonstrations in this Python unit testing tutorial will be black box types (functional unit test) using Selenium; this will assess a few UI functionalities.
We’ll also save the test output in an HTML file using the HTML runner. We pass this to the unittest runner inside the event loop while running the test. You’ll see how to do this as you read along.
Bonus Tip: Besides the HTML runner, you can use the pytest-html module to store your test output as HTML and generate a detailed report. But this requires you to install the pytest and pytest-html modules.
For the first test scenario, we’ll use this React TodoMVC app to test the to-do component. And below is our test scenario:
Test case 1:
Our second test scenario will test the LambdaTest input demo form unit.
Test case 2:
The next action is to inspect each web page to assess the web elements. This is where you get insights into the CSS and web element selectors.
We’ll access the websites via Chrome in this Python unit testing tutorial. But the inspection step is similar in other browsers.
To inspect an element on a webpage, right-click directly on it and select Inspect. You’ll see the attributes of the selected element, including its class, ID name, or any other relevant selector.
Here’s an inspection of the React TodoMVC website:
Hint: The to-do component updates the DOM as you add tasks. So add a dummy task via your browser to make the task element visible for inspection—since you might be adding tasks in the test.
And below is the web element inspection of the demo form website:
However, since we’re running the tests on the LambdaTest cloud grid, you’ll need to get your access key and username from your LambdaTest Automation Dashboard. To do this, click Access Key at the top-right. You’ll see your Username and Access Key in the popped modal box.
Project and Directory Structure
Since this is a functional unit test dealing with WebElements, we’ll adopt the Page Object Model in Python to help modularize our code and improve maintainability.
Here’s the test structure for the Python unit testing project:
Python_unit_test
├─ locators
│ ├─ formLocators.py
│ ├─ todoLocators.py
├─ setup
│ ├─ setup.py
└─ testsuites
├─ test_demo_form.py
├─ test_todo.py
└─ .env
The project has three main directories; locators, setup, and testsuites.
The selected platform for our tests is the Windows OS, and we’ll use Firefox as our test browser. You’ll include these details in the test capability, which you can generate from the Test Capability Generator.
Test setup implementation:
from selenium import webdriver
from dotenv import load_dotenv
import os
load_dotenv('.env')
LT_USERNAME = os.getenv("grid_username")
LT_ACCESS_KEY = os.getenv("access_key")
desired_caps = {
'LT:Options' : {
"user" : os.getenv("grid_username"),
"accessKey" : os.getenv("access_key"),
"build" : "FireTest New",
"name" : "FireBrowser",
"platformName" : os.getenv("test_OS")
},
"browserName" : "FireFox",
"browserVersion" : "103.0",
}
gridURL = "https://{}:{}@hub.lambdatest.com/wd/hub".format(LT_USERNAME, LT_ACCESS_KEY)
class testSet:
def __init__(self) -> None:
self.driver = webdriver.Remote(command_executor=gridURL, desired_capabilities= desired_caps)
def testSetup(self):
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def tearDown(self):
if (self.driver != None):
print("Cleaning the test environment")
self.driver.quit()
Code Walkthrough:
Start by importing the webdriver module from the Selenium package into the setup script. Then import the os and dotenv packages to extract your grid username and access key from your environment variables ( the .env file):
The desired_caps variable is a dictionary of the desired capability specifications for Selenium. This dictionary includes the test name, build name, browser name, and the desired platform’s name.
We also declared the grid URL (gridURL) as shown below:
gridURL = "https://username:acess_key@hub.lambdatest.com/wd/hub"
Here’s how we implement the grid test capabilities and grid URL:
Next, instantiate a remote web driver with the grid URL and the test capabilities inside the init function of the testSet class:
Finally, declare the testSetup and tearDown methods. The testSetup function calls the web driver’s implicitly_wait method, which takes a time value (in seconds). This sets the ground for the test, allowing web elements to load before commencing the test steps. The tearDown function releases the resources used during testing by closing and quitting the browser.
You can learn more about Implicit and Explicit Waits through this blog on Waits in Selenium.
Test case 1 implementation (Todo app unit test):
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class todoLocator:
addTodo = "new-todo"
completeTodo = "toggle"
clearTodo = "clear-completed"
locateTodo = todoLocator()
class todoWebActions:
def __init__(self, driver) -> None:
self.driver=driver
def getWeb(self, URL):
self.driver.get(URL)
def getTitle(self):
return self.driver.title
def addTask(self, task):
self.driver.find_element(By.CLASS_NAME, locateTodo.addTodo).send_keys(task, Keys.ENTER)
def completeTask(self):
self.driver.find_element(By.CLASS_NAME, locateTodo.completeTodo).click()
def clearTask(self):
self.driver.find_element(By.CLASS_NAME, locateTodo.clearTodo).click()
import unittest
import HtmlTestRunner
import sys
sys.path.append(sys.path[0] + "/..")
from setup.setup import testSet
from locators.todoLocators import todoWebActions
set_up = testSet()
todo = todoWebActions(set_up.driver)
class TodoSampleTest(unittest.TestCase):
def test_unit_user_should_able_to_add_item(self):
try:
set_up.testSetup()
todo.getWeb("https://todomvc.com/examples/react/#/")
title = todo.getTitle()
self.assertIn("Todo", title, "Todo is not in title")
todo.addTask("Omisola")
todo.completeTask()
todo.clearTask()
except AssertionError as e:
print("Something went wrong", e)
set_up.tearDown()
if __name__ == "__main__":
unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output='TodoHTML-results'))
Code Walkthrough:
The todoLocators.py file contains the web elements and actions for the todo unit testing. You start by importing the By and Keys classes from the Selenium WebDriver. The By class uses the CSS or XPATH selector for any specified WebElement, while Key invokes Keyboard Actions. We will cover the same at a later point in time in this Python unit testing tutorial.
The todoLocator class holds the web element CSS class names for the todo component. We can call each from this class later. We then instantiate this class within the same script as locateTodo:
Next is the todoLocator class instantiation, followed by the todoWebActions class declaration. Initiating the todoWebActions class with a driver attribute makes it callable with the web driver while running the web actions in the testsuites folder scripts. We then define the URL and title-getter functions using the driver attribute.
Within this class, declare the addTask, completeTask, and clearTask methods. The addTask method accepts a task argument; this is the task you want to add to the component.
The locator in each function then calls the class names from the todoLocator class using its instance. Keys.ENTER is a keyboard action that inserts the given task.
The test_todo.py file is the test case runner for the todo unit test. You need the unittest and HtmlTestRunner modules here. While your test class inherits the TestCase class from unittest, the HtmlTestRunner class collects the test results after execution.
We also import testSet and todoWebActions since we need these in the test. However, we use the sys module to declare the paths for these custom modules. The sys.path.append method calls each file as seen since they’re two levels away from the test_todo.py file.
We also instantiate each class to make them callable. But remember, we need to instantiate todoWebActions with the driver attribute from setup.testSet. This makes the web driver act on the actions declared earlier:
The todoSampleTest class inherits the TestCase class from unittest. Then it defines a test_unit_user_should_able_to_add_item method that runs the Python unit test case. We can call each web action from the todoWebActions instance as todo.webAction.
However, we also insert our test steps in the try/except block to catch the assertion error. Invariably, the test fails if it doesn’t find the declared title in the web page’s title. The test starts with the testSetup method and ends with the tearDown method from the testSet class.
Finally, we run the test in an event loop using unittest. Then we collect each test result as an HTML file into the TodoHTML-results folder:
Execution (ToDo test scenario):
Now, navigate into the testsuites folder and run the test_todo.py file like so:
python test_todo.py
Here’s the execution of the todo app functional unit test on the cloud grid:
And running the generated HTML file in the TodoHTML-results directory:
Test case 2 implementation (Demo form unit test):
from selenium.webdriver.common.by import By
class formLocator:
name = "//input[@id='name']"
email = "//input[@id='inputEmail4']"
password = "//input[@id='inputPassword4']"
company = "//input[@id='company']"
website = "//input[@id='websitename']"
country = "//select[@name='country']"
city = "//input[@id='inputCity']"
address_1 = "//input[@id='inputAddress1']"
address_2 = "//input[@id='inputAddress2']"
state = "inputState"
zip_code = "inputZip"
button = "btn"
locateForm = formLocator()
class formWebAction:
def __init__(self, driver) -> None:
self.driver=driver
def getWeb(self, URL):
self.driver.get(URL)
def getTitle(self):
return self.driver.title
def fillName(self, data):
self.driver.find_element(By.XPATH, locateForm.name).send_keys(data)
def fillEmail(self, data):
self.driver.find_element(By.XPATH, locateForm.email).send_keys(data)
def fillPassword(self, data):
self.driver.find_element(By.XPATH, locateForm.password).send_keys(data)
def fillCompany(self, data):
self.driver.find_element(By.XPATH, locateForm.company).send_keys(data)
def fillWebsite(self, data):
self.driver.find_element(By.XPATH, locateForm.website).send_keys(data)
def fillCountry(self, data):
self.driver.find_element(By.XPATH, locateForm.country).send_keys(data)
def fillCity(self, data):
self.driver.find_element(By.XPATH, locateForm.city).send_keys(data)
def fillAddress1(self, data):
self.driver.find_element(By.XPATH, locateForm.address_1).send_keys(data)
def fillAddress2(self, data):
self.driver.find_element(By.XPATH, locateForm.address_2).send_keys(data)
def fillState(self, data):
self.driver.find_element(By.ID, locateForm.state).send_keys(data)
def fillZipCode(self, data):
self.driver.find_element(By.ID, locateForm.zip_code).send_keys(data)
def submit(self):
self.driver.find_element(By.CLASS_NAME, locateForm.button).click()
import unittest
import HtmlTestRunner
import sys
sys.path.append(sys.path[0] + "/..")
from setup.setup import testSet
from locators.formLocators import formWebAction
set_up = testSet()
form = formWebAction(set_up.driver)
class formSampleTest(unittest.TestCase):
def test_unit_user_should_able_to_fill_form(self):
try:
form.getWeb("https://www.lambdatest.com/selenium-playground/input-form-demo")
set_up.testSetup()
title = form.getTitle()
self.assertIn("Selenium", title, "Selenium is not in title")
form.fillName("Idowu")
form.fillEmail("fakeemail@gmail.com")
form.fillPassword("secret")
form.fillCompany("Lambdatest")
form.fillWebsite("someweb.com")
form.fillCountry("Nigeria")
form.fillCity("A City")
form.fillAddress1("Den street")
form.fillAddress2("Den street")
form.fillState("Lagos")
form.fillZipCode("240100")
form.submit()
except AssertionError as e:
print("Something went wrong", e)
set_up.tearDown()
if __name__ == "__main__":
unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output='DemoFormHTML_results'))
Code Walkthrough:
The formLocators.py file contains all the CSS locators and web actions for the form demo website. Separating the selectors this way makes them easy to change as the web elements change. We start by importing the By selector class from the Selenium WebDriver.
Then we declare a formLocator class to hold all the CSS class names for the web component under test. Besides the state, zip_code, and button attributes, we’ve used Selenium’s XPath locator format for the other web element selectors.
Selenium’s XPath locator helps to locate elements faster within the DOM and takes the form:
XPath = //tagname[@Attribute='Value']
For instance, to locate the country field path within the demo form DOM, we used the @name attribute to locate the element from the //select node.
Here’s a screenshot of the country selector:
So the XPath selector becomes
country = "//select[@name = 'country']"']
We also instantiate this class to make its attributes callable:
The formWebAction class defines all the web actions for the demo form component based on the driver attribute (declared in the init function). Like the ToDo app test case, this one starts by navigating to the test website and getting its title:
The rest of the methods in the formWebAction class fills each form in the test component, ending with a click on the submit button. Each form field method accepts a data argument:
As you can see, the test_demo_form.py resides in the testsuites directory. So it executes the web actions accordingly. Import the necessary modules and custom classes. Then instantiate the testSet and formWebAction class. The formWebAction class instantiates with the web driver attribute from setup.testSet:
The formSampleTest class inherits the TestCase class from unittest and executes the test steps by calling the actions from the formWebAction instance (form). Note how it starts by navigating to the web page and asserting the title using unittest’s built-in assertIn.
Step 1: Navigate to the demo form URL:
Step 2: Fill in the form fields:
The formWebAction instance runs the actions with the driver attribute from set_up. It then executes the test steps inside the try/except block like so:
Step 3: Click the Submit button:
As we did for the previous test case, the tearDown method from the setup.testSet class closes used resources and ends the test.
We also run the test in an event loop using unittest and HtmlTestRunner:
Form demo functional unit test execution:
Open your command line to the testsuites directory and run the test_demo_form.py file like so:
python test_demo_form.py
The Python unit test runs on the cloud grid as shown:
Running the generated HTML test result:
If your goal is to become proficient in automation testing and improve your Python skills, enrolling in a Selenium Python 101 certification program can be an excellent way to begin. Obtaining this certification can give you a strong foundation in using Selenium Python for testing, paving the way for a successful career in this field.
Most successful software products today implement unit testing in their software development pipeline. Failure to perform unit testing while building your app is the first pointer to its potential poor performance. Therefore, it’s better done earlier than later.
While system testing is crucial to your user story, unit testing works behind the scenes to improve your existing architecture and user experience. It also makes your code base more maintainable and easy to debug. Finding it hard to trace bug sources in your software?
If you had used Python, you’ve probably ignored Python unit testing earlier in your development cycle. It’s never too late to start testing the pieces into your code base.
Python unit testing is a software testing technique that tests individual units or components of Python code to ensure they work correctly. Python unit testing aims to isolate each part of the code and test it in isolation to verify its behavior under different conditions.
You can perform Python unit testing using frameworks like unittest, pytest, and nose. Here is a step-by-step guide to performing Python unit testing using unittest: Install the unittest framework, Create a Python module for your unit tests, Import the unittest module, Write test functions, Create a test suite, Run the tests,
Yes, pytest is a Python unit testing framework. It is a popular alternative to the built-in unittest framework that comes with Python.
To test Python code, write test cases covering various scenarios and edge cases. Utilize testing frameworks like unit test or pytest to organize and run your tests. Execute the tests, ensuring expected outputs and behavior. Debug issues encountered and iterated if needed.
To write test cases in Python, first, identify test scenarios from requirements. Next, use 'unittest' or 'pytest' frameworks to create test functions. Write assertions to validate expected outcomes. Execute the tests and analyze results. Lastly, refine tests for better coverage.
To run a Python test, you must first complete a set of steps. To begin, create your test code with a testing framework such as pytest or unittest. Save the test file as.py. Then, launch the terminal or command prompt and navigate to the directory containing the test file. Finally, run the command 'python test_file.py' to run the test. This will start the test and display the results. You can easily run Python tests and ensure the functionality and correctness of your code by following these steps.
You can write unit tests in Python by following a few simple steps. First, import the unittest module, which contains the tools for creating and running tests. Second, make a test class that derives from unittest.The TestCase class. This class will be used to house your test methods. Then, within the class, define the test methods, making sure that each method's name begins with test to indicate that it's a test case. Within these test methods, you can use assertion methods like assertEqual or assertTrue to compare expected results to actual outputs. Finally, to execute the test cases and obtain the results, use unittest.main() or a test runner. Following these steps will assist you in writing more effectively.
When it comes to testing Python code in the terminal, there are a few simple steps you can follow. First, open the terminal or command prompt on your computer. From there, navigate to the directory where your Python file is located. Once you're in the correct directory, type 'python' followed by the name of your Python file with the .py extension. By pressing Enter, you'll execute the code, and the resulting output will be displayed in the terminal. This approach allows you to quickly and conveniently test your Python code and observe the results firsthand.
In Python, to split train and test data, you can use the `train_test_split` function from the `sklearn.model_selection` module. It randomly divides data into training and testing sets with a specified ratio. Ensure your training and evaluation processes are reliable and representative of the data.
Author's Profile
Idowu
Idowu is a self-taught programmer, coding YouTuber, and technical writer with a penchant for teaching. He started his coding career in 2018 but combined it with technical writing a year later and has never looked back. He has worked for a couple of websites over the years, and you'll find some of his coding guides on popular websites like MUO. He loves discussing software structure and coding topics in Python, JavaScript, Golang, and TypeScript. And when it comes to software testing, he's a fan of Playwright and Selenium.
Reviewer's Profile
Shahzeb Hoda
Shahzeb currently holds the position of Senior Product Marketing Manager at LambdaTest and brings a wealth of experience spanning over a decade in Quality Engineering, Security, and E-Learning domains. Over the course of his 3-year tenure at LambdaTest, he actively contributes to the review process of blogs, learning hubs, and product updates. With a Master's degree (M.Tech) in Computer Science and a seasoned expert in the technology domain, he possesses extensive knowledge spanning diverse areas of web development and software testing, including automation testing, DevOps, continuous testing, and beyond.
Get 100 minutes of automation test minutes FREE!!