• Automation
  • Home
  • /
  • Learning Hub
  • /
  • Python Unit Testing: A Complete Tutorial
  • -
  • July 09, 2024

Python Unit Testing: A Complete Tutorial

In this Python unit testing tutorial, learn how to perform unit testing using Python and the unittest framework.

OVERVIEW

Unit testing is a method where individual units or components of a software application are tested independently to ensure they function as intended. When it comes to automated testing, you can use various programming languages, such as Python, to help automate unit testing.

Typically, Python unit testing involves leveraging frameworks such as unittest, which lets you write unit test cases to validate each unit of software applications.

What is Python Unit Testing?

Python lets developers and testers perform unit testing, offering a range of built-in libraries and frameworks like unittest, doctest, and more. These frameworks for Python automation testing simplify the creation and execution of unit tests with advanced features and functionalities, enhancing the testing workflow.

Among these, the unittest module—a core part of Python's standard library—is a complete framework for setting up and executing unit tests. It features test discovery, fixtures, suites, and various assertion methods to evaluate the expected versus actual results.

Why unittest for Python Unit Testing?

unittest is a built-in, open-source Python testing framework that is easy to use, offering many 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… 
      
...

2M+ Devs and QAs rely on LambdaTest

Deliver immersive digital experiences with Next-Generation Mobile Apps and Cross Browser Testing Cloud

Prerequisites for Setting Up Python for Unit Testing

In this Python unit testing tutorial, we will run our test cases on the Windows platform using Python 3.

Install Python and then follow the steps below to set up Python for unit testing:

Create a Project Folder

To create a project folder in VS Code on Windows, follow these steps:

  • Open a folder on your PC.
  • Right-click any blank space, click New, and then Folder.
  • 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.
  • navigate to the folder

Activate Python Virtual Environment

You can run your project globally if you like. However, you want to create a new virtual environment for your test project to avoid dependency interference with the global modules. This isolates your development environment.

Python has a pre-installed virtual environment manager called 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.

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, which we’ll use 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:
  • selenium
    python-dotenv
    html-testrunner
    
  • Navigate to your project root directory from your command line and install the dependencies from the requirements.txt file:
  • pip install -r requirements.txt

Project Structure

The project has three main directories:

  • setup folder: It consists of the setup.py file. This folder holds the entire test suite settings, including the Selenium Grid capabilities. So, the test case shares the setup in this file.
  • locators: It contains the Python scripts holding the locators for the test case considered for the Python unit testing.
  • testsuites: It holds the test runner files for the demo test case. Navigating into this folder and running the test_demo_form.py file executes the unit test case.

We’ll use cloud-based grids to run the test cases.

For this, we will use a cloud grid like LambdaTest. It is a reliable and scalable AI-powered test execution platform that empowers you to perform automation testing across real browsers and operating systems.

Moreover, you can also perform Selenium Python testing in parallel, which shortens your overall build times.

Note

Note : Run Your Selenium Tests With Python on the Cloud. Try LambdaTest Now!

The selected platform for our tests is Windows, and we’ll use Firefox as our test browser. You’ll include these details in the test capability, which you can generate from the LambdaTest Automation Capabilities Generator.

setup/setup.py


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" : "125.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()
LambdaTest

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

import the webdriver module

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 gridURL that connects scripts to the LambdaTest cloud servers.

cloud servers

Finally, declare the testSetup() and tearDown() methods. The testSetup() function calls the 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 Selenium Waits.

How to Perform Python Unit Testing?

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.

If you are new to Selenium, check out our guide on what is Selenium.

Using the HTML runner, we’ll also save the test output in an HTML file. We pass this to the unittest runner inside the event loop while running the test.

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

  • Open the Form Demo website of Selenium Playground.
  • Fill in the form fields.
  • Click the Submit button.

The next action is to inspect each web page to assess the WebElements. This is where you get insights into the CSS and WebElement selectors.

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 Form Demo website:

demo 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 Web Automation Dashboard. To do this, navigate to Settings > Account Settings > Password & Security.

Test Implementation:

locators/formLocators.py


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

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 the XPath locator format for the other selectors.

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.

So the XPath selector becomes.

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

We also instantiate this class to make its attributes callable:

attribute callable

The formWebAction class defines all the web actions for the demo form component based on the driver attribute (declared in the _init_() function). This starts by navigating to the test website and getting its title:

The rest of the methods in the formWebAction class fill each form in the test component, ending with a click on the Submit button. Each form field method accepts a data argument.

testsuites/test_demo_form.py


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

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() methods. The formWebAction() method instantiates with the driver attribute from the setup.testSet.

driver attribute

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 the assertIn() method.

Navigate to the demo form URL using the getWeb() method. The formWebAction instance runs the actions with the driver attribute from set_up. It then executes the test steps inside the try/except block.

Click the Submit button using the submit() method. 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.

submit button

Test Execution:

Open your command line to the testsuites directory and run the test_demo_form.py file:


python test_demo_form.py

The Python unit test runs on the cloud grid is shown below:

cloud grid

Here are the generated HTML test result:

html test result

Best Practices for Python Unit Testing

Here are some best practices for Python unit testing presented in a more conversational tone:

  • One Assertion Per Test: Stick to one assertion per test to pinpoint issues quickly when a test fails, making it easier to spot exactly what went wrong.
  • Independence: Make sure each test stands alone and does not depend on the outcome of any others. This avoids complications from intertwined tests.
  • Isolation: Keep tests isolated from external systems like databases or web services. Use mocking or similar techniques to maintain this isolation.
  • Document Your Tests: Well-documented tests are easier for others to understand and maintain, especially for future modifications or enhancements.
  • Integrate with CI: Incorporate your tests into a Continuous Integration system to run them automatically with code changes, helping catch issues early.
  • Maintain Test Suites: Regularly update your tests to match your evolving codebase and keep them effective and relevant.

In addition, do subscribe to the LambdaTest YouTube Channel and stay updated with the latest video tutorials on Selenium testing, Selenium Python, and more!

Conclusion

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 to do it 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.

Enhance Test Management with AI

Streamline test authoring, management, execution, and reporting to boost efficiency throughout all testing phases.

Automate Accessibility Testing

Frequently asked questions

  • General ...
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.
What is unit testing in Python vs pytest?
Unit testing in Python can be done with the native unittest library, which provides a structured testing framework. pytest, however, is more flexible and powerful, offering a simpler syntax, better fixtures support, and easier integration with other tools and libraries.
How to do unit testing for a class in Python?
To unit test a class in Python, use the unittest framework by defining a class that inherits from unittest.TestCase. Create methods within this class prefixed with test_ to check different aspects of your class's functionality. Use assert methods to validate the results.

Did you find this page helpful?

Helpful

NotHelpful

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud