Вступ до модульного тестування на Python

Ви щойно закінчили писати фрагмент коду і думаєте, що робити. Ви подасте запит на витяг і попросите своїх товаришів по команді переглянути код? Або ви будете перевіряти код вручну?

Ви повинні зробити обидва ці дії, але з додатковим кроком: вам потрібно модульно протестувати свій код, щоб переконатися, що код працює належним чином.

Модульні тести можуть пройти або пройти, і це робить їх чудовою технікою перевірки вашого коду. У цьому підручнику я продемонструю, як писати модульні тести на Python, і ви побачите, як легко їх запустити у вашому власному проекті.

Починаємо

Найкращий спосіб зрозуміти тестування - це практичне виконання. Для цього у файлі з іменем name_function.py я напишу просту функцію, яка бере ім’я та прізвище та повертає повне ім’я:

#Generate a formatted full name def formatted_name(first_name, last_name): full_name = first_name + ' ' + last_name return full_name.title()

Функція formatted_name () приймає ім'я та прізвище та поєднує їх із пробілом між ними, щоб сформувати повне ім'я. Потім він пише велику літеру першої літери кожного слова. Щоб перевірити, чи працює цей код, потрібно написати деякий код, який використовує цю функцію. У names.py я напишу простий код, який дозволяє користувачам вводити свої імена та прізвища:

from name_function import formatted_name print("Please enter the first and last names or enter x to E[x]it.") while True: first_name = input("Please enter the first name: ") if first_name == "x": print("Good bye.") break last_name = input("Please enter the last name: ") if last_name == "x": print("Good bye.") break result = formatted_name(first_name, last_name) print("Formatted name is: " + result + ".")

Цей код імпортує formatted_name () із name_function.py та під час запуску, дозволяє користувачеві вводити ряд імен та прізвищ та показує відформатовані повні імена.

Юніт-тест та тестові кейси

У стандартній бібліотеці Python є модуль, що називається unittest, який містить інструменти для тестування вашого коду. Модульне тестування перевіряє, чи всі конкретні частини поведінки вашої функції правильні, що значно полегшить їх інтеграцію разом з іншими частинами.

Тестовий випадок - це сукупність модульних тестів, що разом доводить, що функція працює за призначенням, у межах цілого ряду ситуацій, в яких ця функція може опинитися і з якими вона повинна працювати. Тестовий випадок повинен враховувати всі можливі типи введення, які функція може отримати від користувачів, і, отже, повинен включати тести, що представляють кожну з цих ситуацій.

Проходження тесту

Ось типовий сценарій написання тестів:

Спочатку потрібно створити тестовий файл. Потім імпортуйте модуль unittest, визначте клас тестування, який успадковується від unittest.TestCase, і, нарешті, напишіть низку методів, щоб перевірити всі випадки поведінки вашої функції.

Пояснення до рядків подано під таким кодом:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger")

По-перше, вам потрібно імпортувати unittest та функцію, яку ви хочете перевірити, formatted_name (). Потім ви створюєте клас, наприклад NamesTestCase, який буде містити тести для вашої функції formatted_name (). Цей клас успадковується від класу unittest.TestCase.

NamesTestCase містить єдиний метод, який перевіряє одну частину formatted_name (). Ви можете викликати цей метод test_first_last_name ().

Пам'ятайте, що кожен метод, який починається з "test_", буде запускатися автоматично, коли ви запускаєте test_name_function.py.

В рамках методу тесту test_first_last_name () ви викликаєте функцію, яку потрібно перевірити, і зберігає повернене значення. У цьому прикладі ми збираємось викликати formatted_name () з аргументами “pete” та “seeger”, і збережемо результат у отриманій змінній.

В останньому рядку ми використаємо метод assert. Метод затвердження перевіряє, що отриманий результат збігається з результатом, який ви очікували отримати. І в цьому випадку ми знаємо, що функція formatted_name () поверне повне ім'я з великими літерами, тому ми очікуємо результат “Піт Сігер”. Щоб перевірити це, використовується метод unittest's assertEqual ().

self.assertEqual(result, “Pete Seeger”)

Цей рядок в основному означає: Порівняйте значення отриманої змінної з “Піт Сігер”, і якщо вони рівні, це нормально, але якщо вони не повідомляють мене.

Під час запуску test_name_function.py ви очікуєте отримати ОК, що означає, що тест пройшов.

Ran 1 test in 0.001s OK

Невдалий тест

Щоб показати вам, як виглядає невдалий тест, я збираюся змінити функцію formatted_name (), включивши новий аргумент середнього імені.

Тож я збираюся переписати функцію так, щоб вона виглядала так:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name, middle_name): full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title()

Ця версія formatted_name () буде працювати для людей з іменами по батькові, але при її тестуванні ви побачите, що функція порушена для людей, які не мають імені.

Отже, коли ви запустите test_name_function.py, ви отримаєте результат, який виглядає приблизно так:

Error Traceback (most recent call last): File “test_name_function.py”, line 7, in test_first_last_name result = formatted_name(“pete”, “seeger”) TypeError: formatted_name() missing 1 required positional argument: ‘middle_name’ Ran 1 test in 0.002s FAILED (errors=1)

На виході ви побачите інформацію, яка розповість вам все, що вам потрібно знати, де тест не вдається:

  • Першим елементом результату є помилка, яка повідомляє, що принаймні один тест у тестовому випадку призвів до помилки.
  • Далі ви побачите файл і метод, у якому сталася помилка.
  • Після цього ви побачите рядок, у якому сталася помилка.
  • І про яку помилку йдеться, у цьому випадку нам бракує 1 аргументу “ім’я-ім’я”.
  • Ви також побачите кількість запущених тестів, час, необхідний для завершення тестів, і текстове повідомлення, яке представляє стан тестів із кількістю помилок, що сталися.

Що робити, якщо тест не пройшов

Проходження тесту означає, що функція поводиться відповідно до того, що від неї очікується. Однак невдалий тест означає, що вас чекає більше задоволення.

Я бачив пару програмістів, які вважають за краще змінити тест замість того, щоб вдосконалювати код - але не до цього. Витратьте трохи більше часу на вирішення проблеми, оскільки це допоможе вам краще зрозуміти код і заощадити час у довгостроковій перспективі.

In this example, our function formatted_name() first required two  parameters, and now as it is rewritten it requires one extra: a middle name. Adding a middle name to our function broke the desired behavior of  it. Since the idea is not to make changes to the tests, the best solution is to make middle name optional.

After we do this the idea is to make the tests pass when the first and last name are used, for example “Pete Seeger”, as well as when first, last and middle names are used, for example “Raymond Red Reddington”. So  let’s modify the code of formatted_name() once again:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name,): if len(middle_name) > 0: full_name = first_name + ' ' + middle_name + ' ' + last_name else: full_name = first_name + ' ' + last_name return full_name.title()

Now the function should work for names with and without the middle name.

And to make sure it still works with “Pete Seeger” run the test again:

Ran 1 test in 0.001s OK
І це те, що я мав намір показати вам: завжди краще вносити зміни до коду відповідно до ваших тестів, ніж навпаки. Зараз прийшов час додати новий тест на імена, які мають по батькові.

Додавання нових тестів

Напишіть новий метод до класу NamesTestCase, який буде перевіряти імена по батькові:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger") def test_first_last_middle_name(self): result = formatted_name("raymond", "reddington", "red") self.assertEqual(result, "Raymond Red Reddington")

Після запуску тесту обоє тестів повинні пройти:

Ran 2 tests in 0.001s OK
Бюстгальтер gjort!

Молодці!

Ви написали свої тести, щоб перевірити, чи функція працює, використовуючи імена з іменем або без нього. Слідкуйте за частиною 2, де я розповім більше про тестування на Python.

Дякую за читання! Перегляньте більше подібних статей у моєму профілі freeCodeCamp: //www.freecodecamp.org/news/author/goran/ та інші цікаві речі, які я створюю на своїй сторінці GitHub: //github.com/GoranAviani