Въведение в модулното тестване в 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 () ще върне пълно име с главни букви, така че очакваме резултата “Pete Seeger”. За да се провери това, се използва методът на unittest's assertEqual ().

self.assertEqual(result, “Pete Seeger”)

Този ред по същество означава: Сравнете стойността в получената променлива с “Pete Seeger” и ако те са равни, всичко е наред, но ако не са ми уведомени.

При изпълнение на test_name_function.py се очаква да получите OK, което означава, че тестът е преминал.

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