Python Unit Testing: A Comprehensive Guide With Best Practices

  • Learning Hub
  • Python Unit Testing: A Comprehensive Guide With Best Practices


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!


The Unittest Framework

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):

Importance of Unit Testing

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:

  • Check if a piece of code, component, function, method, or class handling a logic gives the expected output.
  • Discover and eliminate bugs at the early development stage.
  • Reduce testing costs as the application scales.
  • Track bugs as the application scales.
  • Evaluate the effects of recent modifications on other code units. Thus, it helps reduce or even eliminate regressive bugs.
  • Increase software validity, integrity, and quality, as other programmers or end-users trust a tested product better.
  • It helps developers understand the working mechanism of a piece of code, feature, or component. So it’s an excellent documentation tool.

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.

Unit Testing vs. Component Testing

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.

Unit Testing Best Practices

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:

  • Make your test code simple and readable.
  • Write the test as you develop the software.
  • Prevent logic in test code to safeguard.
  • Tests target singular functions, methods, components, features, or classes.
  • Isolate components for proper unit testing; avoid combined assessments.
  • Prevent component interference in unit tests; avoid assessing multiple units together.
  • Build maintainable test code with Page Object Model (POM). Reduce complexity and codebase.
  • Consider implementing a method to block component branch merging until tests pass.

Mocking in Unit Testing

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.

Prerequisites for Setting up Python for Unit Testing

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.

Install Python

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:

  • Open the Python installation file downloaded earlier and click Add Python to Path to make it executable from the command line.
  • Click Install Now to start the installation.
  • install Python on Windows
  • Wait until the installation completes.
  • Python on Windows setup completion

Create a Project Folder

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:

  • Open a folder on your PC.
  • Right-click any blank space and click New, then Folder.
  • project folder in VS Code on Windows
  • Open VS Code on your computer and click Open Folder.
  • Navigate to the folder you created to open VS Code to this root. You can now create new directories and test scripts within this folder on VS Code.
  • folder you created to open VS Code

Activate Python Virtual Environment

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):


Finally, cd back into your project root folder by running the following command twice consecutively:

cd ..

Here’s the entire command-line activity:

command line activity

Install the Required Dependencies

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


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:

installation process requirements.txt

Demonstration: Python Unit Testing using Unittest Framework

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.

Test Scenarios

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:

  • Open the React to-do web page.
  • Fill out the to-do form to create a task.
  • Complete the added task.
  • Clear the to-do list.

Our second test scenario will test the LambdaTest input demo form unit.

Test case 2:

  • Open the form demo URL.
  • Fill in the form fields.
  • Click the Submit button.

Web Page Inspection

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.

React TodoMVC website

And below is the web element inspection of the demo form website:

web element inspection demo

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:

├─ locators
│  ├─
│  ├─
├─ setup
│  ├─
└─ testsuites
└─ .env

The project has three main directories; locators, setup, and testsuites.

  • The setup folder consists of the file. This folder holds the entire test suite settings, including the Selenium Grid capabilities. So all test cases share the setup in this file.
  • The locators folder contains the Python scripts holding the web element locators for both test sites considered for the Python unit testing.
  • The testsuites directory holds the test runner files for both test cases. Navigating into this folder and running each test file ( and executes the unit test case.

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:

FileName - setup/

from selenium import webdriver

from dotenv import load_dotenv
import os

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://{}:{}".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):
    def tearDown(self):
        if (self.driver != None):
            print("Cleaning the test environment")

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 = ""

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):

FileName - locators/

from 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:

    def getWeb(self, 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()

FileName - testsuites/

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):
            title = todo.getTitle()
            self.assertIn("Todo", title, "Todo is not in title")
        except AssertionError as e:
            print("Something went wrong", e)


if __name__ == "__main__":

Code Walkthrough:

FileName - locators/

The 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.


FileName - testsuites/

The 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 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.

tearDown method

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:

html test runner

Execution (ToDo test scenario):

Now, navigate into the testsuites folder and run the file like so:


Here’s the execution of the todo app functional unit test on the cloud grid:

python cloud grid

And running the generated HTML file in the TodoHTML-results directory:

html test result

Test case 2 implementation (Demo form unit test):

FileName - locators/

from 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:
    def getWeb(self, URL):
    def getTitle(self):
        return self.driver.title
    def fillName(self, data):
    def fillEmail(self, data):
    def fillPassword(self, data):
        self.driver.find_element(By.XPATH, locateForm.password).send_keys(data)
    def fillCompany(self, data):
    def fillWebsite(self, data):
    def fillCountry(self, data):
    def fillCity(self, 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()

FileName - testsuites/

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):
            title = form.getTitle()
            self.assertIn("Selenium", title, "Selenium is not in title")
            form.fillCity("A City")
            form.fillAddress1("Den street")
            form.fillAddress2("Den street")
        except AssertionError as e:
            print("Something went wrong", e)


if __name__ == "__main__":

Code Walkthrough:

FileName - locators/

The 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:

screenshot of the country

So the XPath selector becomes

country = "//select[@name = 'country']"']

We also instantiate this class to make its attributes callable:

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:

form web action

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:

data argument

FileName - testsuites/

As you can see, the 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:

form web action class

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:

Unit test testcase

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:

webaction instance runs

Step 3: Click the Submit button:

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.

tear down method

We also run the test in an event loop using unittest and HtmlTestRunner:

html test runner

Form demo functional unit test execution:

Open your command line to the testsuites directory and run the file like so:


The Python unit test runs on the cloud grid as shown:

python cloud grid

Running the generated HTML test result:

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.

Frequently Asked Questions (FAQs)

What is Python unit testing?

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.

How do you perform unit testing in Python?

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,

Is pytest unit test?

Yes, pytest is a Python unit testing framework. It is a popular alternative to the built-in unittest framework that comes with Python.

How to test python code?

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.

How to write test cases in python?

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.

How to run a python test?

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 Then, launch the terminal or command prompt and navigate to the directory containing the test file. Finally, run the command 'python' 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.

How to write unit tests in python?

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.

How to test python code in terminal?

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.

How to split train and test data in python?

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.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Did you find this page helpful?