Основы тестирования в Python

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Динамическое природа Python делает тестирование крайне важным для большинства приложений. Нет компилятора, который бы выявлял ваши ошибки. Einдинственный способ обнаружить ошибки - запустить код и убедиться, что вы проверили все его функции.

Ассерции

Инструкция assert представляет собой внутреннюю проверку программы. Если выражение ложно, то она вызывает исключение AssertionError.

Синтаксис инструкции assert.

assert <выражение> [, 'Диагностическое сообщение']

Например.

assert isinstance(10, int), 'Ожидалось целое число'

Ее не следует использовать для проверки ввода пользователя (например, данных, введенных в веб-форме или подобном). Она предназначается скорее для внутренних проверок и инвариантов (условий, которые должны всегда быть истинными).

Контрактное программирование

Также известное как проектирование по контракту, широко применение утверждений - это подход к разработке программного обеспечения. Он предполагает, что разработчики программного обеспечения должны определить точные спецификации интерфейса для компонентов программного обеспечения.

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

def add(x, y):
    assert isinstance(x, int), 'Expected int'
    assert isinstance(y, int), 'Expected int'
    return x + y

Проверка входных параметров немедленно выявит вызовчиков, которые не используют соответствующие аргументы.

>>> add(2, 3)
5
>>> add('2', '3')
Traceback (most recent call last):
...
AssertionError: Expected int
>>>

Встроенные тесты

Утверждения также могут использоваться для простых тестов.

def add(x, y):
    return x + y

assert add(2,2) == 4

Таким образом, вы включаете тест в ту же модуль, что и ваш код.

Преимущество: Если код явно сломан, попытки импортировать модуль будут приводить к ошибке.

Это не рекомендуется для исчерпывающего тестирования. Это скорее базовый "тест на冒烟". Работает ли функция вообще на любом примере? Если нет, то что-то явно сломано.

Модуль unittest

Предположим, у вас есть некоторый код в simple.py.

## simple.py

def add(x, y):
    return x + y

Теперь, предположим, вы хотите протестировать его. Создайте отдельный файл для тестирования, например, в /home/labex/project/test_simple.py, следующим образом.

## test_simple.py

import simple
import unittest

Затем определите класс для тестирования.

## test_simple.py

import simple
import unittest

## Обратите внимание, что он наследуется от unittest.TestCase
class TestAdd(unittest.TestCase):
  ...

Класс для тестирования должен наследоваться от unittest.TestCase.

В классе для тестирования вы определяете методы для тестирования.

## test_simple.py

import simple
import unittest

## Обратите внимание, что он наследуется от unittest.TestCase
class TestAdd(unittest.TestCase):
    def test_simple(self):
        ## Тестирование с простыми целыми аргументами
        r = simple.add(2, 2)
        self.assertEqual(r, 5)
    def test_str(self):
        ## Тестирование со строками
        r = simple.add('hello', 'world')
        self.assertEqual(r, 'helloworld')

*Важно: Каждый метод должен начинаться с test.

Использование unittest

unittest предоставляет несколько встроенных утверждений. Каждое из них проверяет разные условия.

## Проверить, что выражение expr истинно
self.assertTrue(expr)

## Проверить, что x равно y
self.assertEqual(x,y)

## Проверить, что x не равно y
self.assertNotEqual(x,y)

## Проверить, что x близко к y
self.assertAlmostEqual(x,y,places)

## Проверить, что вызов callable(arg1,arg2,...) вызывает исключение exc
self.assertRaises(exc, callable, arg1, arg2,...)

Это не исчерпывающий список. В модуле есть и другие утверждения.

Запуск unittest

Для запуска тестов преобразуйте код в сценарий.

## test_simple.py

...

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

Затем запустите Python на файле с тестами.

$ python3 test_simple.py
F.
========================================================
FAIL: test_simple (__main__.TestAdd)
--------------------------------------------------------
Traceback (most recent call last):
  File "testsimple.py", line 8, in test_simple
    self.assertEqual(r, 5)
AssertionError: 4!= 5
--------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)

Комментарий

Эффективное модульное тестирование - это искусство, и оно может стать довольно сложным для крупных приложений.

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

Третьи стороны инструменты для тестирования

Встроенный модуль unittest имеет преимущество того, что он доступен везде - он является частью Python. Однако, многие программисты также считают его довольно избыточным. Популярной альтернативой является pytest. С использованием pytest файл для тестирования упрощается до примерно следующего вида:

## test_simple.py
import simple

def test_simple():
    assert simple.add(2,2) == 4

def test_str():
    assert simple.add('hello','world') == 'helloworld'

Для запуска теста достаточно ввести команду, например, python -m pytest. Затем он найдет и запустит все тесты. Модуль можно установить с помощью pip install pytest.

В pytest есть гораздо больше возможностей, чем в этом примере, но обычно его довольно легко освоить, если вы решите попробовать.

В этом упражнении вы изучите базовые механизмы использования модуля unittest в Python.

В предыдущих упражнениях вы написали файл stock.py, содержащий класс Stock. Для этого упражнения предполагается, что вы используете код, написанный для упражнения 7.9, связанного с типизированными свойствами. Если по какой-то причине это не работает, вы можете скопировать решение из Solutions/7_9 в свою рабочую директорию.

Упражнение 8.1: написание модульных тестов

В отдельном файле test_stock.py напишите набор модульных тестов для класса Stock. Чтобы помочь вам начать, ниже представлен небольшой фрагмент кода, который тестирует создание экземпляра:

## test_stock.py

import unittest
import stock

class TestStock(unittest.TestCase):
    def test_create(self):
        s = stock.Stock('GOOG', 100, 490.1)
        self.assertEqual(s.name, 'GOOG')
        self.assertEqual(s.shares, 100)
        self.assertEqual(s.price, 490.1)

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

Запустите свои модульные тесты. Вы должны получить вывод, похожий на следующий:

.

Ran 1 tests in 0.000s

OK

Как только вы убедитесь, что все работает, напишите дополнительные модульные тесты, которые проверяют следующее:

  • Убедитесь, что свойство s.cost возвращает правильное значение (49010.0).
  • Убедитесь, что метод s.sell() работает правильно. Он должен соответствующим образом уменьшать значение s.shares.
  • Убедитесь, что атрибут s.shares не может быть установлен в нецелое значение.

Для последней части вам понадобится проверить, что возникает исключение. Простой способ сделать это - использовать код, подобный следующему:

class TestStock(unittest.TestCase):
  ...
    def test_bad_shares(self):
         s = stock.Stock('GOOG', 100, 490.1)
         with self.assertRaises(TypeError):
             s.shares = '100'
✨ Проверить решение и практиковаться

Резюме

Поздравляем! Вы завершили лабораторную работу по тестированию. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.