25 ‐ Automated Testing Intro ‐ unittest - MantsSk/CA_PTUA14 GitHub Wiki

Introduction

When developing software, testing is an important part of ensuring that your code works as expected and meets the requirements. In Python, the unittest module provides a framework for writing and running tests to validate the behavior of your code.

With unittest, you can define a set of test cases, each of which tests a specific feature or behavior of your code. Within each test case, you can define a set of test methods that verify that your code behaves as expected under different conditions.

Using unittest allows you to automate your tests, making it easier to run them repeatedly and catch regressions as soon as they occur. unittest also provides powerful features for handling setup and teardown tasks, running test suites, and reporting test results.

Overall, unittest is a powerful and flexible tool for testing your Python code and ensuring that it works correctly. By investing time in writing tests, you can catch bugs early in the development process, save time on manual testing, and ensure that your code is robust and reliable.

Tests placement

In Python projects that use unittest, tests are typically placed in a separate directory named tests or test. Within this directory, tests are organized in a way that reflects the organization of the code being tested.

For example, if your project has a module named my_module.py, you might create a file named test_my_module.py in the tests directory to contain tests for that module.

By separating tests from the main code, you can keep your codebase clean and focused on the main functionality, while the tests can be run independently and modified without affecting the main code.

Examples

Testing a simple function:

import unittest

def add(a, b):
    return a + b

class TestAddFunction(unittest.TestCase):
    
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(0, 0), 0)
        self.assertEqual(add(-1, 1), 0)

Testing a PersonalCode class:

PersonalCode class:

class PersonalCode:
    
    def __init__(self, kodas):
        self.__kodas = kodas
    
    def return_last_four(self):
        return str(self.__kodas)[-4:]
    
    def return_first_four(self):
        return str(self.__kodas)[:4]

    @property
    def kodas(self):
        return self.__kodas

    @kodas.setter
    def kodas(self,new_value):
        self.__kodas = new_value

Testing:

import unittest
import datetime
from personal_code import PersonalCode

class TestKodas(unittest.TestCase):

    def setUp(self):
        self.test_code = "33309240063"
        self.kodas = PersonalCode(self.test_code)

    def test_last_4_personal_code(self):
        self.kodas = PersonalCode(self.test_code) # name 'PersonalCode' is not defined
        self.assertEqual(self.kodas.return_last_four(), self.test_code[-4:]) 

    def test_first_4_personal_code(self):
        self.assertEqual(self.kodas.return_first_four(), self.test_code[:4])

if __name__ == '__main__':
    unittest.main()

Testing an exception:

import unittest

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    return a / b

class TestDivideFunction(unittest.TestCase):
    
    def test_divide(self):
        self.assertEqual(divide(4, 2), 2)
        self.assertRaises(ZeroDivisionError, divide, 4, 0)

There are multiple asserts that you can read about here -> https://kapeli.com/cheat_sheets/Python_unittest_Assertions.docset/Contents/Resources/Documents/index

How to run

In Python, you can run unittest tests using the command python -m unittest. This command will discover all the test cases and test methods in your project and run them.

Here's an example of how to use this command to run tests in a file named test_my_module.py:

python -m unittest tests.test_my_module

This command will run all tests in the tests directory.

Note that if you are using a test runner other than unittest, the command to run tests may be different. Check the documentation for your test runner to learn more.

Exercises

Exercise 1

In this exercise, you will write tests for a simple Python function that manipulates strings. The function reverse_and_upper takes a string as input, reverses it, and converts it to uppercase.

def reverse_and_upper(s):
    return s[::-1].upper()

Write a unit test class for the reverse_and_upper function. Your test class should include the following:

  • A test method to check if the function correctly reverses and converts to uppercase a regular string.
  • A test method to verify the function's behavior with an empty string.
  • A test method to ensure the function handles a string of numbers correctly.
  • (Optional) Any additional test case you think is necessary.

Exercise 2

You will be testing a custom exception in a Python class. The class BankAccount has methods to deposit and withdraw money. A custom exception InsufficientFunds is raised when a withdrawal attempt is made with insufficient funds in the account.

class InsufficientFunds(Exception):
    pass

class BankAccount:
    def __init__(self, balance=0):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFunds("Insufficient funds in the account")
        self.balance -= amount

Write a unit test class for the BankAccount class. Your test class should include:

  • A test method to check successful deposit.
  • A test method to verify successful withdrawal when funds are sufficient.
  • A test method to ensure that an InsufficientFunds exception is raised when trying to withdraw more than the account balance.
  • (Optional) Any additional test cases you find necessary.