Когда вы пишете функцию или класс, вы также можете написать тесты для этого кода. Тестирование доказывает, что ваш код работает так, как должен, в ответ на все типы входных данных, для которых он предназначен. Когда вы пишете тесты, вы можете быть уверены, что ваш код будет работать правильно, когда все больше людей начнут использовать ваши программы. Вы также сможете тестировать новый код по мере его добавления, чтобы убедиться, что ваши изменения не нарушат существующее поведение программы. Каждый программист допускает ошибки, поэтому каждый программист должен часто тестировать свой код, выявляя проблемы до того, как с ними столкнутся пользователи.
Тестирование функции
Чтобы узнать о тестировании, нам нужен код для тестирования. Вот простая функция, которая принимает имя и фамилию и возвращает аккуратно отформатированное полное имя:
def get_formatted_name(first, last):
"""Generate a neatly formatted full name."""
full_name = f"{first} {last}"
return full_name.title()
Функция get_formatted_name()
объединяет имя и фамилию с пробелом между ними для полного имени, а затем набирает заглавными буквами и возвращает полное имя. Чтобы проверить, что get_formatted_name()
работает, давайте создадим программу, использующую эту функцию. Программа names.py позволяет пользователям вводить имя и фамилию и видеть аккуратно отформатированное полное имя:
from name_function import get_formatted_name
print("Enter 'q' at any time to quit.")
while True:
first = input("nPlease give me a first name: ")
if first == 'q':
break
last = input("Please give me a last name: ")
if last == 'q':
break
formatted_name = get_formatted_name(first, last)
print(f"tNeatly formatted name: {formatted_name}.")
Эта программа импортирует get_formatted_name()
из name_function. py. Пользователь может ввести ряд имен и фамилий и увидеть отформатированные полные имена, которые будут сгенерированы:
Enter 'q' at any time to quit.
Please give me a first name: ahmed
Please give me a last name: gouda
Neatly formatted name: Ahmed Gouda.
Please give me a first name: mohammed
Please give me a last name: ali
Neatly formatted name: Mohammed Ali.
Please give me a first name: q
Мы видим, что имена сгенерированы правильно. Но, допустим, мы хотим изменить get_formatted_name()
, чтобы она могла обрабатывать и средние имена. При этом мы хотим убедиться, что не нарушим способ, которым функция обрабатывает имена, содержащие только имя и фамилию. Мы могли бы проверить наш код, запустив names. py и вводя имя, например, Мохаммед Али, каждый раз, когда мы изменяем get_formatted_name()
, но это было бы утомительно. К счастью, Python предоставляет эффективный способ автоматизировать тестирование вывода функции. Если мы автоматизируем тестирование get_formatted_name()
, мы всегда можем быть уверены, что функция сработает, если ей дать те имена, для которых мы написали тесты.
Модульные тесты и тестовые примеры
Модуль unittest из стандартной библиотеки Python предоставляет инструменты для тестирования вашего кода.
- Юнит-тест проверяет правильность одного конкретного аспекта поведения функции.
- Тестовый пример – это набор модульных тестов, которые вместе доказывают, что функция ведет себя так, как должна, в полном диапазоне ситуаций, которые вы ожидаете от нее.
Хороший тестовый пример рассматривает все возможные виды входных данных, которые может получить функция, и включает тесты для представления каждой из этих ситуаций. Тестовый пример с полным покрытием включает в себя полный набор модульных тестов, охватывающих все возможные способы использования функции. Достижение полного покрытия в большом проекте может оказаться сложной задачей. Часто бывает достаточно написать тесты для критических ситуаций в коде, а затем стремиться к полному покрытию, только если проект начинает широко использоваться.
Проходящий тест
Синтаксис для создания тестового примера требует некоторого привыкания, но после создания тестового примера несложно добавить больше модульных тестов для ваших функций. Чтобы написать тестовый пример для функции, импортируйте модуль unittest и функцию, которую вы хотите протестировать. Затем создайте класс, который наследуется от unittest.TestCase
, и напишите серию методов для тестирования различных аспектов поведения вашей функции.
Вот тестовый пример с одним методом, который проверяет, что функция get_formatted_name()
работает правильно, когда ей дается имя и фамилия:
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""Tests for 'name_function.py'."""
def test_first_last_name(self):
"""Do names like 'Mohammed Ali' work?"""
formatted_name = get_formatted_name('Mohammed', 'Ali')
self.assertEqual(formatted_name, 'Mohammed Ali')
if __name__ == '__main__':
unittest.main()
Сначала мы импортируем unittest и функцию, которую хотим протестировать, get_formatted_name(). В class NamesTestCase(unittest.TestCase):
мы создаем класс NamesTestCase, который будет содержать серию модульных тестов для get_formatted_name()
.
Вы можете назвать класс как угодно, но лучше всего назвать его как-то связанным с функцией, которую вы собираетесь тестировать, и использовать в имени класса слово Test. Этот класс должен наследоваться от класса unittest.TestCase
, чтобы Python знал, как запускать написанные вами тесты.
NamesTestCase содержит единственный метод, который тестирует один аспект get_formatted_name()
. Мы называем этот метод test_first_last_name()
, потому что проверяем, что имена, содержащие только имя и фамилию, будут отформатированы правильно. Любой метод, начинающийся с test_
, будет запущен автоматически, когда мы запустим test_name_function. py. В этом тестовом методе мы вызываем функцию, которую хотим протестировать. В этом примере мы вызываем get_formatted_name()
с аргументами ‘Mohammed’ и ‘Ali’ и присваиваем результат в formatted_name formatted_name = get_formatted_name('Mohammed', 'Ali')
.
В self.assertEqual(formatted_name, 'Mohammed Ali')
мы используем одну из самых полезных функций unittest: метод assert. Методы assert проверяют, что полученный вами результат соответствует результату, который вы ожидали получить. В данном случае, поскольку мы знаем, что get_formatted_name()
должен возвращать полное имя, написанное заглавными буквами и с правильным интервалом, мы ожидаем, что значение formatted_name будет Mohammed Ali. Чтобы проверить, так ли это, мы используем метод assertEqual()
от unittest и передаем ему formatted_name и ‘Mohammed Ali’.
В строке self.assertEqual(formatted_name, 'Mohammed Ali')
говорится: “Сравните значение в formatted_name со строкой ‘Mohammed Ali’. Если они равны, как и ожидалось, хорошо. Но если они не совпадают, дайте мне знать!”.
Мы собираемся запустить этот файл напрямую, но важно отметить, что многие механизмы тестирования импортируют ваши тестовые файлы перед их запуском. Когда файл импортируется, интерпретатор выполняет его в процессе импорта. Блок if
if __name__ == '__main__':
unittest.main()
просматривает специальную переменную __name__
, которая устанавливается при выполнении программы. Если этот файл выполняется как основная программа, то значение __name__
устанавливается в __main__
. В этом случае мы вызываем unittest.main()
, которая запускает тестовый пример. Когда механизм тестирования импортирует этот файл, значение __name__
не будет __main__
и этот блок не будет выполнен.
Когда мы запускаем test_name_function. py, мы получаем следующий результат:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Точка в первой строке вывода говорит нам о том, что прошел один тест. Следующая строка говорит нам, что Python выполнил один тест, и его выполнение заняло менее 0,001 секунды. Последнее OK говорит нам, что все модульные тесты в тестовом примере прошли.
Этот вывод показывает, что функция get_formatted_name()
всегда будет работать для имен, содержащих имя и фамилию, если мы не изменим функцию. Когда мы изменим get_formatted_name()
, мы можем снова запустить этот тест. Если тест пройдет, мы будем знать, что функция будет работать для имен типа Мохаммед Али.
Неудачный тест
Как выглядит неудачный тест? Давайте изменим get_formatted_name()
так, чтобы она могла обрабатывать средние имена, но сделаем это таким образом, чтобы сломать функцию для имен с именем и фамилией, таких как Мохаммед Али.
Вот новая версия функции get_formatted_name(), которая требует аргумента среднего имени:
def get_formatted_name(first, middle, last):
"""Generate a neatly formatted full name."""
full_name = f"{first} {middle} {last}"
return full_name.title()
Эта версия должна работать для людей со средними именами, но когда мы ее протестируем, то увидим, что мы сломали функцию для людей с именем и фамилией. На этот раз запуск файла test_name_function. py дает такой результат:
E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_name_function.py", line 8, in test_first_last_name
formatted_name = get_formatted_name('Mohammed', 'Ali')
TypeError: get_formatted_name() missing 1 required positional argument: 'last
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
Здесь очень много информации, потому что многое может понадобиться, если тест не прошел.
Первым элементом в выводе является единственный E
, который говорит нам, что один единичный тест в тестовом примере привел к ошибке.
Далее мы видим, что тест test_first_last_name() в NamesTestCase вызвал ошибку. Знание того, какой тест не сработал, очень важно, когда ваш тестовый пример содержит много модульных тестов.
Затем мы видим стандартный отслеживание, которое сообщает, что вызов функции get_formatted_name(‘Mohammed’, ‘Ali’) больше не работает, потому что в нем отсутствует необходимый позиционный аргумент.
Мы также видим, что был запущен один модульный тест. Наконец, мы видим дополнительное сообщение о том, что общий тест не прошел и что при выполнении теста произошла одна ошибка. Эта информация появляется в конце вывода, поэтому вы видите ее сразу; вам не нужно прокручивать длинный список вывода, чтобы узнать, сколько тестов не удалось выполнить.
Реагирование на неудачный тест
Что делать, если тест не прошел? Если вы проверяете правильные условия, то прохождение теста означает, что функция ведет себя правильно, а провал теста означает, что в новом коде, который вы написали, есть ошибка. Поэтому, если тест не прошел, не меняйте тест. Вместо этого исправьте код, который привел к неудаче теста. Изучите изменения, которые вы только что внесли в функцию, и выясните, как эти изменения нарушили желаемое поведение.
В данном случае get_formatted_name()
раньше требовала только два параметра: имя и фамилию. Теперь требуется имя, среднее имя и фамилия. Добавление обязательного параметра среднего имени нарушило желаемое поведение get_formatted_name(). Лучший вариант – сделать второе имя необязательным. После этого наш тест для таких имен, как Мохаммед Али, снова пройдет, и мы сможем принимать и средние имена.
Давайте изменим get_formatted_name() так, чтобы средние имена стали необязательными, а затем снова запустим тест. Если он пройдет, мы перейдем к проверке того, что функция правильно обрабатывает средние имена.
Чтобы сделать средние имена необязательными, мы перемещаем параметр middle в конец списка параметров в определении функции и даем ему пустое значение по умолчанию. Мы также добавляем тест if, который правильно строит полное имя в зависимости от того, указано ли среднее имя или нет:
def get_formatted_name(first, last, middle=''):
"""Generate a neatly formatted full name."""
if middle:
full_name = f"{first} {middle} {last}"
else:
full_name = f"{first} {last}"
return full_name.title()
В этой новой версии get_formatted_name()
второе имя необязательно. Если в функцию передано второе имя, то полное имя будет содержать имя, среднее и последнее. В противном случае полное имя будет состоять только из имени и фамилии. Теперь функция должна работать для обоих типов имен.
Чтобы узнать, работает ли функция для имен типа Мохаммед Али, запустим test_name_function.py еще раз:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Теперь тест проходит. Это идеально; это означает, что функция снова работает для имен типа Мохаммед Али без необходимости тестировать функцию вручную. Исправить нашу функцию было легко, потому что неудачный тест помог нам определить новый код, который нарушил существующее поведение.
Добавление новых тестов
Теперь, когда мы знаем, что get_formatted_name()
снова работает для простых имен, давайте напишем второй тест для людей, включающих второе имя. Для этого мы добавим еще один метод в класс NamesTestCase:
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""Tests for 'name_function.py'."""
def test_first_last_name(self):
"""Do names like 'Mohammed Ali' work?"""
formatted_name = get_formatted_name('Mohammed', 'Ali')
self.assertEqual(formatted_name, 'Mohammed Ali')
def test_first_last_middle_name(self):
"""Do names like 'Ahmed Mohammed Gouda' work?"""
formatted_name = get_formatted_name('Ahmed', 'Gouda', 'Mohammed')
self.assertEqual(formatted_name, 'Ahmed Mohammed Gouda')
if __name__ == '__main__':
unittest.main()
Мы назовем этот новый метод test_first_last_middle_name()
. Имя метода должно начинаться с test_
, чтобы метод запускался автоматически, когда мы запускаем test_name_function. py. Мы называем метод так, чтобы было понятно, какое поведение get_formatted_name()
мы тестируем. В результате, если тест не сработает, мы сразу узнаем, какие типы имен будут затронуты. Вполне нормально иметь длинные имена методов в классах TestCase. Они должны быть описательными, чтобы вы могли понять смысл вывода при неудачном тестировании, а поскольку Python вызывает их автоматически, вам никогда не придется писать код, вызывающий эти методы.
Для тестирования функции мы вызываем get_formatted_name() с первым, последним и средним именем formatted_name = get_formatted_name('Ahmed', 'Gouda', 'Mohammed')
, а затем используем assertEqual()
, чтобы проверить, что возвращаемое полное имя совпадает с ожидаемым полным именем (первое, среднее и последнее). Когда мы снова запустим test_name_function. py, оба теста пройдут:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Отлично! Теперь мы знаем, что функция по-прежнему работает для таких имен, как Мохаммед
Ali, и мы можем быть уверены, что она будет работать и для таких имен, как Ahmed Mohammed Gouda.
Тестирование класса
Мы только что написали тесты для одной функции. Теперь вы напишете тесты для класса. Вы будете использовать классы во многих своих программах, поэтому полезно иметь возможность доказать, что ваши классы работают правильно. Если у вас есть проходящие тесты для класса, над которым вы работаете, вы можете быть уверены, что улучшения, которые вы внесете в класс, случайно не нарушат его текущее поведение.
Разнообразие методов assert
Python предоставляет ряд методов assert в классе unittest.TestCase
. Как упоминалось ранее, методы assert проверяют, действительно ли условие, которое вы считаете истинным в определенной точке вашего кода, является истинным. Если условие верно, как и ожидалось, ваше предположение о том, как ведет себя эта часть программы, подтверждается; вы можете быть уверены в отсутствии ошибок. Если условие, которое вы считаете истинным, на самом деле не истинно, Python выдает исключение.
В таблице ниже описаны шесть часто используемых методов assert. С помощью этих методов можно проверить, что возвращаемые значения равны или не равны ожидаемым значениям, что значения True или False, а также что значения находятся или не находятся в заданном списке. Вы можете использовать эти методы только в классе, который наследуется от unittest.TestCase
, поэтому давайте рассмотрим, как можно использовать один из этих методов в контексте тестирования реального класса.
Метод | Использовать |
---|---|
assertEqual(a, b) | Убедиться, что a == b |
assertNotEqual(a, b) | Убедитесь, что a != b |
assertTrue(x) | Убедитесь, что x является истинным |
assertFalse(x) | Проверить, что x – это False |
assertIn(item, list) | Убедитесь, что элемент находится в списке |
assertNotIn(item, list) | Убедитесь, что элемент не находится в списке |
Класс для тестирования
Тестирование класса похоже на тестирование функции – большая часть вашей работы заключается в проверке поведения методов класса. Но есть и несколько отличий, поэтому давайте напишем класс для тестирования. Рассмотрим класс, который помогает проводить анонимные опросы:
class AnonymousSurvey:
"""Collect anonymous answers to a survey question."""
def __init__(self, question):
"""Store a question, and prepare to store responses."""
self.question = question
self.responses = []
def show_question(self):
"""Show the survey question."""
print(self.question)
def store_response(self, new_response):
"""Store a single response to the survey."""
self.responses.append(new_response)
def show_results(self):
"""Show all the responses that have been given."""
print("Survey results:")
for response in self.responses:
print(f"- {response}")
Этот класс начинается с вопроса опроса, который вы предоставляете def __init__(self, question):
и включает пустой список для хранения ответов. Класс имеет методы для печати вопроса def show_question(self):
, добавления нового ответа в список ответов def store_response(self, new_response):
и печати всех ответов, хранящихся в списке def show_results(self):
.
Чтобы создать экземпляр этого класса, достаточно предоставить вопрос. После создания экземпляра, представляющего конкретный опрос, вы можете отобразить вопрос опроса с помощью show_question()
, сохранить ответ с помощью store_response()
и показать результаты с помощью show_results()
.
Чтобы показать, что класс AnonymousSurvey работает, давайте напишем программу, которая использует этот класс:
Эта программа определяет вопрос (“На каком языке вы впервые научились говорить?”) и создает объект AnonymousSurvey с этим вопросом. Программа вызывает show_question() для отображения вопроса, а затем запрашивает ответы. Каждый ответ сохраняется по мере его получения. Когда все ответы введены (пользователь вводит q для выхода), show_results() печатает результаты опроса:
What language did you first learn to speak?
Enter 'q' at any time to quit.
Language: Arabic
Language: English
Language: English
Language: q
Thank you to everyone who participated in the survey!
Survey results:
- Arabic
- English
- English
Этот класс работает для простого анонимного опроса. Но допустим, мы хотим улучшить AnonymousSurvey и модуль, в котором он находится, – survey. Мы можем разрешить каждому пользователю вводить более одного ответа. Мы можем написать метод, который будет перечислять только уникальные ответы и сообщать, сколько раз был введен каждый ответ. Можно написать еще один класс для управления неанонимными опросами.
Внесение таких изменений может повлиять на текущее поведение класса AnonymousSurvey. Например, возможно, что, пытаясь разрешить каждому пользователю вводить несколько ответов, мы можем случайно изменить способ обработки одиночных ответов. Чтобы не нарушить существующее поведение в процессе разработки модуля, мы можем написать тесты для класса.
Тестирование класса AnonymousSurvey
Давайте напишем тест, который проверит один из аспектов поведения AnonymousSurvey. Мы напишем тест, проверяющий правильность хранения одного ответа на вопрос опроса. Мы воспользуемся методом assertIn()
для проверки того, что ответ находится в списке ответов после его сохранения:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey"""
def test_store_single_response(self):
"""Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
self.assertIn('English', my_survey.responses)
if __name__ == '__main__':
unittest.main()
Мы начинаем с импорта модуля unittest и класса, который мы хотим протестировать, AnonymousSurvey. Мы называем наш тестовый пример TestAnonymousSurvey
, который снова наследуется от unittest.TestCase
. Первый метод тестирования проверит, что когда мы сохраняем ответ на вопрос опроса, этот ответ попадает в список ответов опроса. Хорошее описательное название для этого метода – test_store_single_response()
. Если тест завершится неудачно, мы узнаем по имени метода, показанному в результатах неудачного теста, что возникла проблема с сохранением одного ответа на опрос.
Чтобы протестировать поведение класса, нам нужно создать его экземпляр. В my_survey = AnonymousSurvey(question)
мы создаем экземпляр под названием my_survey с вопросом “На каком языке вы впервые научились говорить?”. Мы сохраняем один ответ, английский, используя метод store_response(). Затем мы проверяем, что ответ был сохранен правильно, утверждая, что English находится в списке my_survey.responses self.assertIn('English', my_survey.responses)
.
Когда мы запускаем test_survey.py, тест проходит:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Это хорошо, но опрос полезен только в том случае, если он генерирует более одного ответа. Давайте проверим, что три ответа могут быть сохранены правильно. Для этого мы добавим еще один метод в TestAnonymousSurvey:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey"""
def test_store_single_response(self):
"""Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
self.assertIn('English', my_survey.responses)
def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
responses = ['Arabic', 'English', 'Spanish']
for response in responses:
my_survey.store_response(response)
for response in responses:
self.assertIn(response, my_survey.responses)
if __name__ == '__main__':
unittest.main()
Мы вызываем новый метод test_store_three_responses()
. Мы создаем объект опроса так же, как и в методе test_store_single_response(). Мы определяем список, содержащий три различных ответа responses = ['Arabic', 'English', 'Spanish']
, а затем вызываем store_response() для каждого из этих ответов. После сохранения ответов мы пишем еще один цикл и утверждаем, что каждый ответ теперь находится в my_survey.responses for response in responses:
.
Когда мы снова запускаем test_survey. py, оба теста (для одного ответа и для трех ответов) проходят:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Все работает отлично. Однако эти тесты немного повторяются, поэтому мы воспользуемся другой функцией unittest, чтобы сделать их более эффективными.
Метод setUp()
В test_survey. py мы создали новый экземпляр AnonymousSurvey в каждом методе теста, и в каждом методе мы создали новые ответы. Класс unittest.TestCase
имеет метод setUp()
, который позволяет вам создать эти объекты один раз и затем использовать их в каждом из ваших тестовых методов. Когда вы включаете метод setUp()
в класс TestCase
, Python запускает метод setUp()
перед запуском каждого метода, начинающегося с test_
. Любые объекты, созданные в методе setUp()
, затем доступны в каждом написанном вами методе тестирования.
Давайте используем setUp() для создания экземпляра опроса и набора ответов, которые могут быть использованы в test_store_single_response() и test_store_three_responses():
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey."""
def setUp(self):
"""
Create a survey and a set of responses for use in all test methods.
"""
question = "What language did you first learn to speak?"
self.my_survey = AnonymousSurvey(question)
self.responses = ['Arabic', 'English', 'Spanish']
def test_store_single_response(self):
"""Test that a single response is stored properly."""
self.my_survey.store_response(self.responses[0])
self.assertIn(self.responses[0], self.my_survey.responses)
def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
for response in self.responses:
self.my_survey.store_response(response)
for response in self.responses:
self.assertIn(response, self.my_survey.responses)
if __name__ == '__main__':
unittest.main()
Метод setUp()
делает две вещи: создает экземпляр опроса self.my_survey = AnonymousSurvey(question)
, и создает список ответов self.responses = ['Arabic', 'English', 'Spanish']
. Каждый из этих ответов имеет префикс self, поэтому их можно использовать в любом месте класса. Это упрощает оба метода тестирования, поскольку ни один из них не должен создавать экземпляр опроса или ответа.
Метод test_store_single_response() проверяет, что первый ответ в self.responses-self.responses[0]
– может быть сохранен правильно, а test_store _three_responses() проверяет, что все три ответа в self.responses
могут быть сохранены правильно.
Когда мы снова запускаем test_survey. py, оба теста по-прежнему проходят. Эти тесты будут особенно полезны при попытке расширить AnonymousSurvey для обработки нескольких ответов для каждого человека. Изменив код для приема нескольких ответов, вы можете запустить эти тесты и убедиться, что не нарушили возможность хранения одного ответа или серии отдельных ответов.
Когда вы тестируете собственные классы, метод setUp() может облегчить написание методов тестирования. Вы создаете один набор экземпляров и атрибутов в setUp(), а затем используете эти экземпляры во всех своих тестовых методах. Это намного проще, чем создавать новый набор экземпляров и атрибутов в каждом тестовом методе.
Когда тест запущен, Python печатает один символ для каждого юнит-теста по мере его завершения. Прошедший тест печатает точку, тест, который приводит к ошибке, печатает E, а тест, который приводит к неудачному утверждению, печатает F. Вот почему вы увидите разное количество точек и символов в первой строке вывода при запуске тестовых примеров. Если тест занимает много времени, потому что содержит много модульных тестов, вы можете просмотреть эти результаты, чтобы получить представление о том, сколько тестов проходит.