
Содержание статьи
Дмитрий Расторгуев

Я разработчик-самоучка, умеющий писать простые программы. Но мне нужно признаться. Невозможно упомянуть, как все взаимосвязано в моей голове.
Эта ситуация усугубляется, если я возвращаюсь к коду, который написал через несколько дней. Оказывается, эту проблему можно решить, следуя методологии разработки, управляемой тестированием (TDD).
Что такое TDD и почему это важно?
Если говорить непрофессионалом, то TDD рекомендует писать тесты, которые проверяли бы функциональность вашего кода перед написанием фактического кода. Только когда вы удовлетворены своими тестами и проверяемыми ими функциями, вы начинаете писать фактический код, чтобы удовлетворить условия, наложенные тестом, которые позволят им пройти.
Выполнение этого процесса гарантирует, что вы тщательно спланируете написанный код, чтобы пройти эти тесты. Это также предотвращает возможность отложения написания тестов на более позднюю дату, поскольку они могут считаться ненужными по сравнению с дополнительными функциями, которые могут быть созданы в течение этого времени.
Тесты также придают вам уверенность, когда вы начинаете рефакторинг кода, поскольку вы, скорее всего, обнаружите ошибки благодаря мгновенной обратной связи во время выполнения тестов.

Как начать?
Чтобы начать писать тесты на Python, мы будем использовать unittest
модуль, поставляемый с Python. Для этого создаем новый файл mytests.py
где будут размещаться все наши тесты.
Начнем с привычного «привет, мир»:
import unittest
from mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello_world(), 'hello world')
Обратите внимание, что мы импортируем helloworld()
функция от mycode
файл. В файле mycode.py
сначала мы просто включим следующий код, который создает функцию, но на этом этапе ничего не возвращает:
def hello_world():
pass
Бег python mytests.py
сгенерирует такой выход в командной строке:
F
====================================================================
FAIL: test_hello (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 7, in test_hello
self.assertEqual(hello_world(), 'hello world')
AssertionError: None != 'hello world'
--------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
Это ясно указывает на то, что тест провалился, чего и ожидали. К счастью мы уже написали тесты, поэтому мы знаем, что он всегда будет рядом, чтобы проверить эту функцию, что дает нам уверенность в выявлении потенциальных ошибок в будущем.
Чтобы гарантировать прохождение кода, давайте изменим mycode.py
к следующему:
def hello_world():
return 'hello world'
Бег python mytests.py
снова мы получаем следующий выход в командной строке:
.
--------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Поздравляю! Вы только что написали свой первый тест. Теперь перейдем к несколько более сложной задаче. Мы создадим функцию, которая позволит нам создать нестандартное понимание числового списка в Python.
Давайте начнем с написания теста для функции, которая создавала бы список определенной длины.
В файле mytests.py
это был бы метод test_custom_num_list
:
import unittest
from mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello_world(), 'hello world')
def test_custom_num_list(self):
self.assertEqual(len(create_num_list(10)), 10)
Это проверит функцию create_num_list
возвращает список длиной 10. Давайте создадим функцию create_num_list
в mycode.py
:
def hello_world():
return 'hello world'
def create_num_list(length):
pass
Бег python mytests.py
сгенерирует такой выход в командной строке:
E.
====================================================================
ERROR: test_custom_num_list (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 14, in test_custom_num_list
self.assertEqual(len(create_num_list(10)), 10)
TypeError: object of type 'NoneType' has no len()
--------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)
Это соответствует ожиданиям, поэтому давайте изменим функцию create_num_list
в mytest.py
чтобы пройти тест:
def hello_world():
return 'hello world'
def create_num_list(length):
return [x for x in range(length)]
Выполняется python mytests.py
в командной строке демонстрирует, что второй тест также пройден:
..
--------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Давайте теперь создадим специальную функцию, которая будет трансформировать каждое значение в списке следующим образом: const * ( X ) ^ power
. Сначала давайте напишем тест для этого, используя метод test_custom_func_
который принял бы значение 3 как X, взял его в степень 3 и умножил на константу 2, в результате чего получится значение 54:
import unittest
from mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello_world(), 'hello world')
def test_custom_num_list(self):
self.assertEqual(len(create_num_list(10)), 10)
def test_custom_func_x(self):
self.assertEqual(custom_func_x(3,2,3), 54)
Давайте создадим функцию custom_func_x
в файле mycode.py
:
def hello_world():
return 'hello world'
def create_num_list(length):
return [x for x in range(length)]
def custom_func_x(x, const, power):
pass
Как и ожидалось, мы получаем ошибку:
F..
====================================================================
FAIL: test_custom_func_x (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 17, in test_custom_func_x
self.assertEqual(custom_func_x(3,2,3), 54)
AssertionError: None != 54
--------------------------------------------------------------------
Ran 3 tests in 0.000s
FAILED (failures=1)
Функция обновления custom_func_x
чтобы пройти тест, мы имеем следующее:
def hello_world():
return 'hello world'
def create_num_list(length):
return [x for x in range(length)]
def custom_func_x(x, const, power):
return const * (x) ** power
Проведя тесты снова, мы получаем пропуск:
...
--------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Наконец, давайте создадим новую функцию, включающую custom_func_x
функцию в понимании списка Как обычно, начнем с написания теста. Заметьте, что для уверенности мы включаем два разных случая:
import unittest
from mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello_world(), 'hello world')
def test_custom_num_list(self):
self.assertEqual(len(create_num_list(10)), 10)
def test_custom_func_x(self):
self.assertEqual(custom_func_x(3,2,3), 54)
def test_custom_non_lin_num_list(self):
self.assertEqual(custom_non_lin_num_list(5,2,3)[2], 16)
self.assertEqual(custom_non_lin_num_list(5,3,2)[4], 48)
Теперь давайте создадим функцию custom_non_lin_num_list
в mycode.py
:
def hello_world():
return 'hello world'
def create_num_list(length):
return [x for x in range(length)]
def custom_func_x(x, const, power):
return const * (x) ** power
def custom_non_lin_num_list(length, const, power):
pass
По-прежнему мы получаем ошибку:
.E..
====================================================================
ERROR: test_custom_non_lin_num_list (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 20, in test_custom_non_lin_num_list
self.assertEqual(custom_non_lin_num_list(5,2,3)[2], 16)
TypeError: 'NoneType' object has no attribute '__getitem__'
--------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (errors=1)
Чтобы пройти тест, давайте обновим mycode.py
файл к следующему:
def hello_world():
return 'hello world'
def create_num_list(length):
return [x for x in range(length)]
def custom_func_x(x, const, power):
return const * (x) ** power
def custom_non_lin_num_list(length, const, power):
return [custom_func_x(x, const, power) for x in range(length)]
Проводя тесты в последний раз, мы проходим их все!
....
--------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
Поздравляю! На этом вступление к тестированию на Python завершается. Обязательно ознакомьтесь с приведенными ниже ресурсами, чтобы узнать больше о тестировании в целом.
Код доступен здесь на GitHub.
Полезные ресурсы для дальнейшего обучения!
Веб-ресурсы
Ниже приведены ссылки на некоторые библиотеки, посвященные тестированию в Python:
YouTube видео
Если вы не хотите читать, рекомендую просмотреть следующие видео на YouTube.