Unittest.TestCase.assertEqual: примеры (PYTHON)

Руководство по использованию assertEqual в модуле unittest
Раздел: Тестирование, Утверждения
unittest.TestCase.assertEqual(first, second, msg): None

Основы метода assertEqual

Метод assertEqual принадлежит классу unittest.TestCase и предназначен для проверки равенства двух объектов в модульном тестировании. Его применение актуально в сценариях, где требуется убедиться, что фактический результат работы функции или метода соответствует ожидаемому значению.

Синтаксис и параметры:
Основная форма вызова: self.assertEqual(first, second, msg=None).

  • first: фактическое значение, полученное в ходе теста.
  • second: ожидаемое значение, с которым происходит сравнение.
  • msg (необязательный): строка сообщения, которая выводится при провале теста (AssertionError). Если параметр не указан, генерируется стандартное сообщение об ошибке.

Метод не возвращает значения. При успешном сравнении выполнение теста продолжается. В случае неравенства объектов возбуждается исключение AssertionError.

Внутренняя реализация использует оператор == для сравнения. Для проверки неравенства существует парный метод assertNotEqual.

Базовые примеры применения

Простейший случай сравнения чисел.

import unittest

class TestMath(unittest.TestCase):
    def test_sum(self):
        self.assertEqual(5 + 3, 8)

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Сравнение строк с использованием необязательного сообщения.

import unittest

class TestString(unittest.TestCase):
    def test_greeting(self):
        name = 'Alex'
        greeting = f'Hello, {name}'
        self.assertEqual(greeting, 'Hello, Alex', msg='Строки не совпадают')

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Сравнение списков. Метод корректно работает с коллекциями.

import unittest

class TestList(unittest.TestCase):
    def test_squares(self):
        result = [x**2 for x in range(5)]
        self.assertEqual(result, [0, 1, 4, 9, 16])

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Пример проваленного теста, демонстрирующий вывод.

import unittest

class TestFail(unittest.TestCase):
    def test_division(self):
        self.assertEqual(10 / 2, 4)  # Ожидается 5

if __name__ == '__main__':
    unittest.main()
F
======================================================================
FAIL: test_division (__main__.TestFail)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 5, in test_division
    self.assertEqual(10 / 2, 4)
AssertionError: 5.0 != 4

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

Похожие методы в Python

Библиотека unittest предлагает несколько специализированных методов сравнения.

assertNotEqual(first, second, msg=None): проверяет, что два значения не равны. Противоположность assertEqual.

self.assertNotEqual(1, 2)  # Тест пройдет

assertTrue(expr, msg=None) и assertFalse(expr, msg=None): проверяют истинность или ложность выражения. Более общие, но менее информативны при провале, чем assertEqual для сравнения булевых значений.

self.assertTrue(1 == 1)  # Эквивалентно self.assertEqual(1, 1)
self.assertFalse(1 == 2)

assertIs(first, second, msg=None): проверяет идентичность объектов (оператор is). Используется для сравнения с None или проверки, что это один и тот же объект в памяти.

self.assertIs(None, some_var)  # Предпочтительнее self.assertEqual для None
self.assertIs(a, b)  # Проверка, что a и b - один объект

assertAlmostEqual(first, second, places=7, msg=None, delta=None) и assertNotAlmostEqual: предназначены для сравнения чисел с плавающей точкой с учетом погрешности (places - знаки после запятой, delta - абсолютная погрешность). Более надежны, чем assertEqual для float.

self.assertAlmostEqual(0.1 + 0.2, 0.3, places=7)  # Тест пройдет
# self.assertEqual(0.1 + 0.2, 0.3)  # Может упасть из-за погрешности

Выбор метода: Для сравнения конкретных значений на равенство используют assertEqual. Для проверки истинности сложного выражения - assertTrue/False. Для сравнения чисел с плавающей точкой - assertAlmostEqual. Для проверки идентичности, особенно с None, - assertIs/IsNot.

Аналоги в других языках программирования

JavaScript (Jest, Node.js assert): В Jest используется функция expect().toBe() для строгого равенства (аналог assertIs) и expect().toEqual() для рекурсивного сравнения значений (аналог assertEqual).

// Jest
test('addition', () => {
  expect(2 + 2).toBe(4); // Строгое равенство
  expect([1, 2]).toEqual([1, 2]); // Глубокое сравнение
});
Тест проходит

Java (JUnit): Используются статические методы класса org.junit.Assert, например assertEquals(expected, actual). Порядок аргументов (ожидаемое, фактическое) часто обратный по сравнению с Python.

import org.junit.Assert;
import org.junit.Test;

public class TestExample {
    @Test
    public void testSum() {
        Assert.assertEquals(4, 2 + 2);
    }
}
Тест проходит

PHP (PHPUnit): Метод $this->assertEquals($expected, $actual). Как и в Python, поддерживает необязательный третий аргумент - сообщение.

public function testSum(): void
{
    $this->assertEquals(4, 2 + 2, 'Сложение не работает');
}
Тест проходит

C# (NUnit/xUnit): В NUnit: Assert.AreEqual(expected, actual). В xUnit: Assert.Equal(expected, actual).

// xUnit
[Fact]
public void TestSum()
{
    Assert.Equal(4, 2 + 2);
}
Тест проходит

Golang (testing): Используются функции из пакета testing, например, if got != want { t.Errorf(...) }. Специального метода assert нет, идиоматично использовать простое сравнение.

func TestSum(t *testing.T) {
    got := 2 + 2
    want := 4
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }
}
Тест проходит

Kotlin (Kotlin Test/JUnit): assertEquals(expected, actual) из kotlin.test.

import kotlin.test.Test
import kotlin.test.assertEquals

class TestExample {
    @Test
    fun testSum() {
        assertEquals(4, 2 + 2)
    }
}
Тест проходит

Lua (luaunit): lu.assertEquals(actual, expected). Порядок аргументов аналогичен Python.

require('luaunit')

function testSum()
    lu.assertEquals(2 + 2, 4)
end

os.exit(lu.LuaUnit.run())
Тест проходит

SQL (PL/pgSQL, Unit Testing): В рамках процедурных расширений, например, для PostgreSQL в pgTAP есть функция is().

BEGIN;
SELECT plan(1);
SELECT is(2 + 2, 4, '2+2 should be 4');
SELECT * FROM finish();
ROLLBACK;
ok 1 - 2+2 should be 4

Основные отличия от Python: порядок аргументов (ожидаемое/фактическое), наличие или отсутствие встроенного модуля тестирования, строгость сравнения (значение vs идентичность).

Распространенные ошибки

1. Неверный порядок аргументов. Фактический результат часто путают с ожидаемым, что усложняет чтение сообщений об ошибках.

import unittest

class TestOrder(unittest.TestCase):
    def test_order(self):
        result = some_function()  # Возвращает 5
        # Нежелательно: сначала ожидаемое, потом фактическое
        self.assertEqual(5, result)  # Работает, но неидеально
        # Предпочтительный порядок: self.assertEqual(result, 5)

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

2. Сравнение чисел с плавающей точкой. Прямое сравнение float из-за погрешностей ведет к нестабильным тестам.

import unittest

class TestFloat(unittest.TestCase):
    def test_float_fail(self):
        self.assertEqual(0.1 + 0.2, 0.3)  # Может упасть

    def test_float_correct(self):
        self.assertAlmostEqual(0.1 + 0.2, 0.3, places=7)

if __name__ == '__main__':
    unittest.main()
.F
======================================================================
FAIL: test_float_fail (__main__.TestFloat)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 5, in test_float_fail
    self.assertEqual(0.1 + 0.2, 0.3)
AssertionError: 0.30000000000000004 != 0.3

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

3. Сравнение разнотипных объектов, которые могут быть равны при приведении. Python допускает сравнение некоторых разных типов (например, 1 == True, 0 == False).

import unittest

class TestType(unittest.TestCase):
    def test_int_bool(self):
        self.assertEqual(1, True)  # Тест пройдет, но это может быть неочевидно
        # Более строгая проверка: self.assertIs(1, True) упадет

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

4. Использование assertEqual для проверки на None. Хотя это работает, предпочтительнее использовать специализированный assertIs.

import unittest

def get_value():
    return None

class TestNone(unittest.TestCase):
    def test_none(self):
        value = get_value()
        self.assertEqual(value, None)  # Работает
        self.assertIs(value, None)     # Более идиоматично и читаемо

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

Изменения в последних версиях

В основных версиях Python метод assertEqual не претерпевал значительных синтаксических изменений, так как является фундаментальным для фреймворка unittest. Однако связанные улучшения присутствуют.

Python 3.2: Добавлен метод assertNotIn и другие, расширяющие набор, но не меняющие поведение assertEqual.

Python 3.4: В сообщениях об ошибках для assertMultiLineEqual (который иногда вызывается из assertEqual для строк) улучшено отображение различий.

Python 3.11: Повышена производительность модуля unittest в целом, что косвенно касается и выполнения assertEqual. Добавлены более информативные сообщения об ошибках для некоторых типов данных.

Важно отметить, что сама логика сравнения через оператор == остается неизменной. Основные обновления касаются окружающей инфраструктуры, улучшения диагностических сообщений и производительности.

Расширенные и специализированные примеры

Сравнение сложных структур данных (словарей со вложенными списками). Метод рекурсивно сравнивает элементы.

Пример python
import unittest

class TestComplexStructures(unittest.TestCase):
    def test_config(self):
        expected_config = {
            'version': 1,
            'params': {'max_users': 100, 'roles': ['admin', 'user']},
            'enabled': True
        }
        actual_config = {
            'version': 1,
            'params': {'max_users': 100, 'roles': ['admin', 'user']},
            'enabled': True
        }
        self.assertEqual(actual_config, expected_config)

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Использование с пользовательскими классами. Для корректной работы необходимо определить метод __eq__.

Пример python
import unittest

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __eq__(self, other):
        if not isinstance(other, Person):
            return False
        return self.name == other.name and self.age == other.age

class TestCustomClass(unittest.TestCase):
    def test_person(self):
        p1 = Person('Ivan', 30)
        p2 = Person('Ivan', 30)
        self.assertEqual(p1, p2)  # Без __eq__ тест бы упал

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Сравнение множеств (set). Порядок элементов не важен.

Пример python
import unittest

class TestSet(unittest.TestCase):
    def test_set_equality(self):
        set1 = {1, 2, 3, 4}
        set2 = {4, 3, 2, 1}
        self.assertEqual(set1, set2)  # Тест пройдет

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Сравнение объектов datetime.

Пример python
import unittest
from datetime import datetime, timezone

class TestDateTime(unittest.TestCase):
    def test_datetime(self):
        dt1 = datetime(2023, 10, 5, 12, 0, 0, tzinfo=timezone.utc)
        dt2 = datetime(2023, 10, 5, 12, 0, 0, tzinfo=timezone.utc)
        self.assertEqual(dt1, dt2)

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Проверка возвращаемого значения функции. Самый частый сценарий использования.

Пример python
import unittest

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

class TestFactorial(unittest.TestCase):
    def test_factorial_of_zero(self):
        self.assertEqual(factorial(0), 1)
    def test_factorial_of_five(self):
        self.assertEqual(factorial(5), 120)

if __name__ == '__main__':
    unittest.main()
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Сравнение сгенерированных последовательностей, например, генераторов. Генератор необходимо преобразовать в список для сравнения.

Пример python
import unittest

class TestGenerator(unittest.TestCase):
    def test_generator(self):
        gen = (x for x in range(3))
        # self.assertEqual(gen, [0,1,2])  # Упадет, так как сравниваются разные типы
        self.assertEqual(list(gen), [0, 1, 2])  # Корректно

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

питон unittest.TestCase.assertEqual function comments

En
Unittest.TestCase.assertEqual Check equality assertion