Skip to content

TestSuite и TestRunner

Основные концепции

Тестовый случай (TestCase) — это класс, который наследуется от unittest.TestCase. В этом классе определяются методы, которые начинаются с префикса test_, каждый из которых представляет собой отдельный тест.

Тестовый набор (TestSuite) — это коллекция тестовых случаев или других тестовых наборов. Используется для группировки тестов и управления их порядком выполнения.

Тестовый исполнитель (TestRunner) — это компонент, который управляет выполнением тестов и предоставляет результаты. TextTestRunner — это стандартный текстовый тестовый исполнитель, который выводит результаты тестов в консоль.

testSuite - Тестовый набор

testSuite — это коллекция тестов, которые можно выполнять вместе. Это полезно, когда нужно сгруппировать тесты по определенным критериям, например, по функциональности или модулям.

Пример объединения тестовых классов

python
import unittest

class TestMathOperations(unittest.TestCase):
    """Тестирование математических операций."""

    def test_addition(self) -> None:
        """Тестирование операции сложения."""
        self.assertEqual(1 + 1, 2)

class TestStringMethods(unittest.TestCase):
    """Тестирование строковых методов."""

    def test_upper(self) -> None:
        """Тестирование метода upper()."""
        self.assertEqual('foo'.upper(), 'FOO')

    def test_lower(self) -> None:
        """Тестирование метода lower()."""
        self.assertEqual('foo'.lower(), 'foo')

def get_suite() -> unittest.TestSuite:
    """Создание TestSuite, включающей все тесты."""
    suite = unittest.TestSuite()
    case = unittest.TestLoader()
    suite_1 = case.loadTestsFromTestCase(TestMathOperations)
    suite.addTest(suite_1)
    suite_2 = case.loadTestsFromTestCase(TestStringMethods)
    suite.addTest(suite_2)
    return suite

runner = unittest.TextTestRunner(verbosity=2)
runner.run(get_suite())

Пример объединения отдельных тестов

python
import unittest

def add(a: int, b: int) -> int:
    """Addition function"""
    return a + b

def multiply(a: int, b: int) -> int:
    """Multiply function"""
    return a * b

class TestMathOperations(unittest.TestCase):
    """Tests for math operations"""

    def test_add(self) -> None:
        """Test that the add function works correctly"""
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 1), 0)
        self.assertEqual(add(-1, -1), -2)

    def test_multiply(self) -> None:
        """Test that the multiply function works correctly"""
        self.assertEqual(multiply(3, 2), 6)
        self.assertEqual(multiply(-1, 1), -1)
        self.assertEqual(multiply(-1, -1), 1)

# Создаем TestSuite
suite = unittest.TestSuite()
suite.addTest(TestMathOperations('test_add'))
suite.addTest(TestMathOperations('test_multiply'))

# Создаем TextTestRunner и запускаем тесты
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)

TextTestRunner и verbosity

TextTestRunner — это один из способов запуска тестов, который выводит результаты в текстовом виде. Параметр verbosity (многословие) управляет уровнем детализации выводимой информации.

  • verbosity=0: только количество ошибок и пропущенных тестов.
  • verbosity=1 (по умолчанию): выводит краткую информацию о пройденных и не пройденных тестах, и общее время выполнения.
  • verbosity=2: выводит детальную информацию о каждом тесте.

Пример с разным уровнем verbosity

python
import unittest

class TestVerbosity(unittest.TestCase):
    """Tests to demonstrate verbosity"""

    def test_success(self) -> None:
        """A test that should pass"""
        self.assertEqual(1 + 1, 2)

    def test_failure(self) -> None:
        """A test that should fail"""
        self.assertEqual(1 + 1, 3)

        
# Запуск с verbosity=0
runner = unittest.TextTestRunner(verbosity=0)
suite = unittest.TestLoader().loadTestsFromTestCase(TestVerbosity)
runner.run(suite)        
        
# Запуск с verbosity=1
runner = unittest.TextTestRunner(verbosity=1)
suite = unittest.TestLoader().loadTestsFromTestCase(TestVerbosity)
runner.run(suite)

# Запуск с verbosity=2
runner = unittest.TextTestRunner(verbosity=2)
suite = unittest.TestLoader().loadTestsFromTestCase(TestVerbosity)
runner.run(suite)

Упражнения

Упражнение 1: Тестирование функции деления

Создайте функцию divide(a: int, b: int) -> float, которая делит одно число на другое, и напишите тесты для этой функции, включая случай деления на ноль.

Упражнение 2: Использование setUp и tearDown

Напишите класс тестов, в котором используются методы setUp и tearDown для инициализации и очистки ресурсов перед и после каждого теста.

Упражнение 3: Создание TestSuite

Создайте несколько классов тестов и объедините их в TestSuite. Запустите TestSuite с помощью TextTestRunner.

Упражнение 4: Тестирование исключений

Напишите тесты для функции, которая должна генерировать исключения в определенных случаях. Используйте метод assertRaises.

Упражнение 5: Параметризация тестов

Используйте метод subTest для параметризации тестов. Например, напишите один тест для проверки нескольких пар значений для функции add.

Упражнение 6: Использование внешних данных

Напишите тесты, которые используют внешние данные (например, из файла или базы данных) для проверки функциональности вашего кода.

Упражнение 7: Настройка verbosity

Экспериментируйте с разными значениями verbosity в TextTestRunner и наблюдайте, как меняется вывод результатов тестирования.

Упражнение 1: Тестирование функции factorial

Напишите тесты для функции factorial, которая вычисляет факториал числа.

python
def factorial(n: int) -> int:
    """Вычисление факториала числа n."""
    if n == 0:
        return 1
    return n * factorial(n - 1)

Упражнение 2: Тестирование класса Stack

Создайте класс Stack с методами push, pop, is_empty и напишите тесты для каждого метода.

python
class Stack:
    """Стек на основе списка."""

    def __init__(self) -> None:
        self.items: list = []

    def push(self, item: int) -> None:
        """Добавление элемента в стек."""
        self.items.append(item)

    def pop(self) -> int:
        """Удаление и возврат верхнего элемента стека."""
        if not self.is_empty():
            return self.items.pop()
        raise IndexError("pop from empty stack")

    def is_empty(self) -> bool:
        """Проверка, пуст ли стек."""
        return len(self.items) == 0

Упражнение 3: Тестирование исключений

Напишите тесты для функции divide, которая делит два числа и вызывает ValueError, если делитель равен нулю.

python
def divide(a: int, b: int) -> float:
    """Деление a на b."""
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

Упражнение 4: Тестирование класса Rectangle

Создайте класс Rectangle с методами area и perimeter и напишите тесты для каждого метода.

python
class Rectangle:
    """Прямоугольник с заданными шириной и высотой."""

    def __init__(self, width: int, height: int) -> None:
        self.width = width
        self.height = height

    def area(self) -> int:
        """Вычисление площади прямоугольника."""
        return self.width * self.height

    def perimeter(self) -> int:
        """Вычисление периметра прямоугольника."""
        return 2 * (self.width + self.height)

Упражнение 5: Тестирование функции find_max

Напишите тесты для функции find_max, которая находит максимальное значение в списке чисел.

python
def find_max(numbers: list[int]) -> int:
    """Поиск максимального значения в списке numbers."""
    if not numbers:
        raise ValueError("List is empty")
    return max(numbers)

Упражнение 6: Тестирование функции is_palindrome

Напишите тесты для функции is_palindrome, которая проверяет, является ли строка палиндромом.

python
def is_palindrome(s: str) -> bool:
    """Проверка, является ли строка s палиндромом."""
    return s == s[::-1]

Упражнение 7: Тестирование функции flatten

Напишите тесты для функции flatten, которая преобразует вложенный список в плоский.

python
def flatten(lst: list) -> list:
    """Преобразование вложенного списка в плоский."""
    result = []
    for item in lst:
        if isinstance(item, list):
            result.extend(flatten(item))
        else:
            result.append(item)
    return result

Contacts: teffal@mail.ru