• Automation
  • Home
  • /
  • Learning Hub
  • /
  • Running Python Unit Tests With unittest: A Beginner's Guide
  • -
  • October 23, 2024

Running Python Unit Tests With unittest: A Beginner's Guide

In this tutorial, learn how to run Python unit tests using the unittest framework, along with real-world examples.

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 Use unittest Framework?

unittest is a Python-based framework inspired by the JUnit framework. It provides a framework for writing and running automated tests.

Some of its key features include:

  • Easy Test Implementation: It provides a TestCase base class that identifies all tests and allows execution with a single command.
  • Command-Line Interface: It offers a CLI to identify and run tests with a project. The code python -m unittest discover identifies and runs tests, including those in nested directories. The discovered tests can be executed by specifying a module, class, or a single test function.
  • Resource Initialization and De-Allocation: It provides the SetUp() function to specify which resources are necessary for test execution. The tearDown() function is used to free up or delete the created resources after execution completes. These functions are pre and post-executed. They are used in the following fashion:
  • Create Subtests: The subTest module creates multiple subtests for each parameter, allowing the test case to continue even if some parameters throw an error. This tests all parameters and shows which ones fail.

How to Run Python Unit Tests?

Since the unittest framework comes pre-installed with Python, you'll need to install Python if you haven't already. Download the latest version of Python from the official Python website. After downloading, run the installer and follow the on-screen instructions.

To verify the installation, open your terminal and type:

python --version

To verify if unittest is available, create a .py file and run the following line of code:

import unittest

Once all installations are verified, we can write our test cases.

Configuring Tests

To achieve better scalability and reliability, we will run our tests on the cloud. For this, we will use cloud-based testing platforms such as LambdaTest. It is a reliable and scalable AI-powered test execution platform that empowers you to perform the unittest testing using Python across real browsers and operating systems.

Note

Note : Run Selenium tests with Python on the cloud. Try LambdaTest Now!

You can get your LambdaTest Username and Access Key from your LambdaTest Account Settings > Password & Security. It is also required to install the python-dotenv package in order to read the .env file. Paste your LambdaTest credentials in the .env file as shown below:

LT_USERNAME= <username>
LT_ACCESS_KEY= <access_key>

In the following code snippet, we'll define the options related to Chrome, which will serve as metadata in a JSON format, as shown below:

//lt_options.json
{
"build": "Build: Python Unittest Demo",
"project": "Project: Python Unittest Demo",
"name": "Test: Python Unittest Demo",
"w3c": true,
"selenium_version": "latest",
"plugin": "python-python",
"platform": "Windows 11",
"browserName": "Chrome",
"version": "latest",
"visual": false,
"network": true,
"console": true,
"video": true
}
LambdaTest

The JSON file defines configuration parameters for our testing environment in Chrome, such as the Selenium version to use and the platform to test on. The LambdaTest Automation Capabilities Generator can be used to generate the appropriate configurations for the test environment.

We'll load the provided JSON and pass it to ChromeOptions. Then, we'll execute a remote WebDriver instance to run Selenium tests on the LambdaTest platform, utilizing the credentials. You can find the code snippet in the pyunitsetup.py file.

self.driver = webdriver.Remote(


command_executor=f"https://{lt_username}:{lt_access_key}@hub.lambdatest.com/wd/hub",
    options=chrome_options
        )


    # self.driver = webdriver.Chrome()

Each test initializes the PyUnitTestSetup class, which includes instantiating a driver and passing the lt_options to configure remote test execution. This setup is particularly useful for cross browser testing and can be configured to work with cloud testing platforms like LambdaTest.

Writing Tests

Let’s use the LambdaTest eCommerce Playground to test different test scenarios to check different functionalities such as login, card badge update, add to cart, continue shopping, and logout.

You can find the test script for these test cases in the unittest_with_selenium.py file.

  • Login: This login functionality checks valid credentials and confirms redirection to the My Account page of the LambdaTest eCommerce Playground. A teardown process ensures proper cleanup after test execution.
  • Cart Badge Update: This test scenario involves adding an item to the cart and checking if the cart badge updates accordingly. It ensures that the cart count reflects the addition of items, providing validation for a working cart. Similar to other tests, it includes a teardown process to clean up afterward.
  • Add to Cart: This test verifies the functionality of adding an item to the cart by ensuring that the correct item is displayed in the cart after addition. It confirms that the added item appears as expected. Like other tests, it concludes with a teardown process for cleanup.

    It follows the same process as the cart badge update test by selecting and adding the item to the cart. After adding the item to the cart, we review the cart and validate the name of the item added to the cart with what was actually added.

  • Continue Shopping: This test checks the workflow of adding items to the cart, viewing the cart, and then continuing shopping. It checks if users can effortlessly transition between adding items to the cart and browsing more products. Afterward, a clean-up process, like for other tests.

    After adding the element to the cart, we go to the cart, press the Continue Shopping button, and validate if we are on the home page. We use the ActionChains object that allows you to create chains of actions (like mouse movements, clicks, etc.) and then execute them as a single action. It's useful for performing complex interactions that involve mouse movements or keyboard actions.

  • Logout: This test evaluates the logout functionality of the application, ensuring that users can successfully log out. It verifies the effectiveness of the logout operation by confirming the presence of logout confirmation. Similar to other tests, this one also includes a teardown process to clean up after completion.

    First, we'll log in using the previous credentials with the existing login function logic. After this, we will log out of the account.

...

2M+ Devs and QAs rely on LambdaTest

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

Running Tests

Now, we will run our Python unit tests both sequentially and in parallel.

Sequential Execution

Before we run the tests sequentially, we will need to wrap our previous functions in a class inherited from TestCase. We can wrap all the functions in a single class, but since we also need to show parallel execution, we will create two classes.

class TestLoginPage(unittest.TestCase):


     def test_login(self):
         … … … …


     def test_logout(self):
         … … … …

Similarly, we can create for the other three functions:

class TestPlatform(unittest.TestCase):


  def test_cart_badge_update(self):
      … … … …


  def test_add_to_cart_functionality(self):
      … … … …


  def test_continue_shopping(self):
      … … … …

Now that we have the test case defined wrapped in a class inherited with the TestCase class, we can execute test cases from the terminal.

Run the following command to execute the test:

python -m unittest unittest_with_selenium.py

Output:

running-test-output

Parallel Execution

To run the test cases in parallel, we will use a multiprocessing package in Python. We will create separate threads for our two test case suites and run them independently. A fair note using this approach is to make sure that the test cases are independent of each other.

import unittest
import multiprocessing


# Import your test classes here
from unittest_with_selenium import TestLoginPage,TestPlatform


# List of test classes to run
test_classes = [TestLoginPage, TestPlatform]


def run_tests(test_class):
    suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
    unittest.TextTestRunner().run(suite)


if __name__ == '__main__':
    processes = []
    # for test_class in [MyTest, MyTest2]:  # Add more test classes here if needed
    for test_class in test_classes:  # Add more test classes here if needed
        process = multiprocessing.Process(target=run_tests, args=(test_class,))
        processes.append(process)
        process.start()


    for process in processes:
        process.join()

After running the scripts from above, the tests will also be logged on to the LambdaTest Web Automation dashboard as seen below:

parallel-execution1

As we can see, all 5 tests are executed and are shown on the dashboard. This is what is displayed on the terminal for a sequential run.

You can further deep dive into the tests and see the events executed.

parallel-execution2

Best Practices for Python Unit Testing

Now that we understand how unit testing is performed in Python, here are a few key best practices to follow for optimized results. With these best practices, you can make your unit tests using the unittest framework in Python more efficient, readable, and maintainable.

  • Organize Test Cases With Test Suites: Use the TestSuite() method to group related tests, making it easier to manage and run specific subsets of tests.
  •  import unittest
     def suite():
           suite = unittest.TestSuite()
           suite.addTest(unittest.makeSuite(YourTestClass))
           return suite
    
  • Leverage Fixtures: Use the setUp() method to prepare the environment before each test and the tearDown() method to clean up after each test.
  • class MyTest(unittest.TestCase):
        def setUp(self):
            self.driver = webdriver.Chrome()
    
    
        def tearDown(self):
            self.driver.quit()
    
    
        def test_example(self):
            self.driver.get("https://www.example.com")
    
    
    if __name__ == "__main__":
        unittest.main()
    
  • Use Assert Methods: Take advantage of the various assert methods provided by the unittest framework for more precise testing.
  • self.assertEqual(a, b)
    self.assertTrue(x)
    self.assertFalse(x)
    self.assertRaises(Exception, func, *args, **kwargs)
    
  • Use Test Discovery: Use the test discovery feature to automatically find and run tests. You can use the following command for this:
  • python -m unittest discover
  • Implement Mocking for Dependencies: Use the unittest.mock module to mock dependencies and control their behavior in tests.
  • from unittest.mock import MagicMock
    mock = MagicMock(return_value=10)
    mock.method()
    
  • Use Parameterized Tests: Use the subTest module for parameterizing tests within a single test method.

    In the below example, the @parameterized.expand decorator runs the test_parameterized method with different sets of parameters. Each tuple in the list represents an input `i` and expected output, allowing you to test the i % 2 operation with various values and verify the results.

  • import unittest
    from parameterized import parameterized
    class MyTest(unittest.TestCase):
        @parameterized.expand([
            (0, 0),
            (1, 1),
            (2, 0),
            (3, 1),
            (4, 0),
        ])
        def test_parameterized(self, i, expected):
            self.assertEqual(i % 2, expected)
    
    
    if __name__ == "__main__":
        unittest.main()
    
  • Use Test Loaders for Custom Test Loading: Customize test loading using the TestLoader() method for more control over which tests are run.
  • loader = unittest.TestLoader()
    tests = loader.loadTestsFromTestCase(MyTest)
    testRunner = unittest.TextTestRunner()
    testRunner.run(tests)
    
  • Document Tests: Write clear and concise documentation for all tests. Also, add instructions for adding new tests and a debugging guide in case any tests fail.
  • Isolate Tests: Tests should be granular, i.e., a test for a function should not depend on another function's results. Although this may not always be possible, it is a decision that must be considered while developing and writing tests.

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

Conclusion

Unit testing is one of the key testing practices that developers follow to ensure a robust codebase. It allows developers to test their code granularly and ensure that all modules (units) behave as expected.

Python provides a native unittest framework that comes natively installed with the language. This framework can be used to write and execute Python unit tests with a single command. The framework provides various functionalities, such as built-in logical assertions, executing all tests with a single command, and functions for resource initialization and deallocation.

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

  • General ...
What is a Python unit test?
A Python unit test is a method for testing individual units of source code, typically functions or methods, to ensure they work as expected. It is used to isolate and verify that each part of the program functions correctly.
Is pytest better than unittest?
pytest is often considered better than unittest for its simpler syntax, advanced features like fixtures, and rich plugin ecosystem. However, unittest is part of Python’s standard library and is widely used for basic testing needs.
How to write unit tests for Python classes?
To write unit tests for Python classes, you create test methods within a test class that inherits from unittest.TestCase or use pytest directly. Each test method verifies the behavior of specific class methods or properties.
Which Python unit test framework to use?
The choice depends on the project’s complexity. Use unittest for simple, built-in solutions, and opt for pytest if you need more flexibility, plugins, and an easier syntax for writing tests.

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