Простое вступление в тестовую разработку с помощью Python

1656674766 prostoe vstuplenie v testovuyu razrabotku s pomoshhyu python

Дмитрий Расторгуев

1*ak94Xh5LGvl2TgtnXPQQhQ

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

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

Что такое TDD и почему это важно?

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

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

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

1*FZGakHQbCUMAyDinf-KBiw
Общий рабочий процесс 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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *