In today's world of software development, automation, and scripting, implementing tests in code should just be a default. Not only does it help ensure code quality and the code is doing what it's supposed to be, but it's basic decency for any script or program. There are several ways to test code, but what are they?

In this blog post you'll learn about unit testing with Python. You'll dive into what unit testing actually is and how to implement it into Python code.

Prerequisites

To follow along with this blog post, you should have the following:

  • An understanding of Python at a beginner to intermediate level.
  • A text editor or IDE - Visual Studio code (VS Code) will be used for this blog post.

Whats a Unit Test?

Before jumping into the code and seeing it live, you may be wondering what a unit test is.

A unit test, in short, is a way to confirm the code is doing what it's supposed to be doing. It tests the smallest unit possible in the code to confirm the code is interacting with the platform or the application as expected.

Some of those smaller units include:

  • Methods
  • Functions
  • Properties

For example, let's say you have a function that does addition, 1 + 1. That function should be returning an answer of two. A unit test, for example, would test that function and confirm it's in fact returning two. If it doesn't return two, the unit test would fail and tell you why.

You may be wondering But can't I just run the code and confirm it's doing what it's supposed to be doing? Sure, you can, but think about it at scale. Let's take two scenarios:

  • You have multiple pieces of code that you're working on. Do you really want to be manually testing each piece? That's not only cumbersome, but incredibly time consuming.
  • You test the code manually, but because code is always changing, a change breaks the code. Even though you tested it manually, it went through another iteration after you tested it. How can you keep up with testing all of those moving parts manually?

The Unittest Library

Now that you know what a unit test is, what Python library will be used? There are a few different Python libraries for testing, but the one that's very popular is called unittest. Unittest is a built-in library, meaning it comes packaged with Python, so you don't have to worry about installing anything.

The Unittest framework supports test automation, aggregation tests, mock tests, and multiple different methods within the test cases.

To achieve multiple tests, unittest supports concepts in an object-oriented way. Let's take a look at four:

  • test fixture - A test fixture is the preparation needed to perform either one test or multiple tests, including any cleanup needed.
  • test case - Test case is the individual unit of testing, for example, a function.
  • test suite - The suite is a collection of multiple tests that are executed together.
  • test runner - Runner is a component that orchestrates execution of tests and provides the result to the user.

An example of a unit test is shown below. Let's take a look at the code and break it down.

  • The unittest library gets imported
  • A new class is created. The name of the class can be anything you prefer, but the most important aspect is the unittest.TestCase. This calls the unittest test case suite for testing of individual tests. Calling that class is expected and needed for a unit test in a test case perspective.
  • The function must start with test. If you look up different test case examples, all of the methods that create tests start with test. This is mandatory and unittest expects it. If test isn't first in the method name, unittest will skip over it.
  • assetEqual is a type of test in the unittest.TestCase class. It has two values and checks if they equal each other. If not, the test fails.
import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO'

Writing the Code

Now that you've seen an example of a unit test, let's take a look at the code that you'll be testing with. Although it is a simple Python application, multiple types of testing can be done with it.

If you haven't heard of it, there is a development practice called Test Driven Development (TTD). With TTD, you write the tests first and then write the code based off of the tests. Although not many follow it, it's definitely good to know what it is because it'll give you different testing ideas and another way to look at development.

Below is the Python code that will be tested. Let's break it down.

Open up Visual Studio Code and add in the following code. Ensure that the Python file name is called hello.py. This will be needed for the test.

  • The logging library is imported so we can use the error method for any exceptions.
  • The hello() function asks for two inputs - first name and last name. Once the inputs are given to Python, Python prints out Hello firstname lastname.
  • Error handling is done with try/except blocks. The except block is utilizing the Exception class.
import logging

def hello():
    first = input('First Name: ')
    last = input('Last Name: ')

    try:
        print(f"Hello {first} {last}")

        return f"Hello {first} {last}"
    
    except Exception as e:
        logging.error(e)
        
if __name__ == '__main__':
    hello()
else:
    print('Running as an imported script')
    hello()

The code above and the unit test code, which you will see in the next section, must be in the same directory.

Testing the Code

Now it's time to dive into the unit test itself and see what's available and how to test the hello() function.

Below is the unit test for testing the Python hello() function. Ensure that VS Code is open and you paste the code in below to a new Python file in the same directory as the hello() function.

Although not mandatory, all test files should start with a name of test. It's a best practice in the Python community and that's how others will be able to differentiate a test from an application.

Let's break down the code:

  • Two imports occur, the unittest library and the hello Python file/application.
  • A new class is created to initiate unittest.TestCase.
  • Three methods exist for testing.
import unittest
import hello

class TestHello(unittest.TestCase):
    
    def test_hello_fail_unless(self):
        self.failUnless(True)
    
    def test_fail_if_not_mike(self):
        first = 'mike'
        self.assertEqual(first, 'mike')
    
    def test_mike_lower(self):
        first = 'mike'
        self.assertTrue(first.islower())

The first method is to ensure the test fails unless True. This means, if the code fails, the test should automatically fail right off the bat.

The second method is to fail the test if the first name is not mike. It is using an assertEqual method which tests the left value with the right value and is comma separated.

The third method is to test if mike is in lower case, using the assertTrue method.

Now that we know what the methods are doing, let's run the code. Within the VS Code terminal and inside of the directory with both test_hello.py and hello.py, run the following command to kick off the test.

python3 -m unittest test_hello.py

The output should look similar to the below, letting you know that the test ran successfully after you input the first and last values.

Congratulations! You have successfully run your first unit test using Python.

Conclusion

In this blog post you dove in and learned fundamentals of getting started with unit testing Python code. You first looked at what unit testing is and why it's important in today's world. You then learned about the unittest library that's available in the standard Python library, so no installation is required. Finally, you learned that any code can be tested, even small functions, and the actual test itself.

For your next challenge, try unit testing a Python application that you've already written, or plan to write. See the different ways that unittest can help in your development lifecycle.