Часть третья: написание базовых тестов
Эта статья является частью серии. Если вы еще не сделали этого, ознакомьтесь с предыдущими статьями:
Часть первая: зачем мы тестируем
Часть вторая: основные концепции тестирования
Часть третья: написание базовых тестов
Часть четвертая: запуск ваших тестов
Часть пятая: инструменты и методы эффективного тестирования
Часть шестая: параметризация и тестирование на основе свойств
Часть седьмая: тестирование с отслеживанием состояния
Пора писать наши первые тесты! Мы будем тестировать функциональность в простом контракте ERC20. Если вы хотите использовать Solidity, вы можете создать локальную копию контракта с помощью следующей команды:
brownie bake token
или, если вы предпочитаете Vyper:
brownie bake vyper-token
Обратите внимание, что каждый из этих проектов поставляется с полным набором тестов. Не стесняйтесь просматривать файлы в каталоге tests/
, чтобы найти еще несколько примеров.
Написание наших первых тестов
Начнем с простого теста:
- В строке 1 мы импортируем объекты Brownie, необходимые для теста.
accounts
дает нам доступ к финансируемым и разблокированным счетам, аToken
используется для развертывания нашего контракта ERC20. - Строка 3 - это начало нашего теста. При использовании pytest все функции, начинающиеся с
test_
, считаются тестовыми. - В строках 4 и 5 мы выполняем нашу тестовую настройку. Сначала мы развертываем
Token
контракт, а затем ведем учет балансаaccounts[0]
, который мы будем использовать позже в нашем утверждении. - В строке 7 мы выполняем тестируемое действие, передавая 10¹⁸ токенов с
accounts[0]
наaccounts[1]
. - В строке 9 мы делаем наше утверждение: баланс токенов
accounts[0]
должен уменьшиться ровно на 10¹⁸.
Здесь происходит довольно много всего, но как только вы освоитесь, синтаксис станет довольно простым.
А теперь время для второго теста:
Ключевое различие между этим тестом и первым состоит в том, что в первом мы пошли по «счастливому пути» - по тому, где код выполняется успешно; но в этом тесте мы ожидаем возврата транзакции.
Важный раздел здесь - это строки 7 и 8, где мы используем диспетчер контекста brownie.reverts
. Чтобы тест прошел, транзакция в диспетчере контекста должна вернуться с указанным сообщением об ошибке. Обратите внимание, что указывать сообщение об ошибке необязательно - мы могли бы просто сказать brownie.reverts()
в качестве обобщающего слова для любой откатывающейся транзакции.
Светильники
Мы только прикоснулись к тестируемому поведению передаточной функции. Наш тест подтвердил изменение баланса отправителя; следующий очевидный шаг - убедиться, что баланс получателя также изменяется.
Наш новый тест будет почти идентичен первому, поэтому у вас может возникнуть соблазн скопировать-вставить и внести несколько изменений:
Это работает, но мы повторяем здесь код. Теперь у нас будет три теста, которые начнутся с развертывания Token
контракта. Позже, если мы изменим аргументы конструктора для нашего контракта, нам придется изменять эту строку в каждом из наших тестов. В большом наборе тестов это может потребовать много работы!
К счастью, pytest предлагает решение этой проблемы, известное как фикстур. Приспособления - это функции, которые применяются к одному или нескольким тестам и вызываются перед выполнением каждого теста. Они используются для установки начальных условий, необходимых для теста.
Чтобы создать прибор, мы применяем декоратор @pytest.fixture
к функции. Давайте создадим приспособление для развертывания нашего контракта:
Затем мы передаем результат прибора в наши тесты, добавляя имя прибора в качестве входного аргумента теста:
Дублированного кода больше нет, и наши тесты теперь легче поддерживать!
Совместное использование приборов в тестовых модулях
Если вам требуется одно и то же устройство в нескольких тестовых файлах, вы можете переместить его в файл с именем conftest.py
в той же папке или в общей родительской папке. Pytest автоматически обнаруживает эти фикстуры и делает их доступными - вам не нужно ничего импортировать вручную.
Связывание приспособлений
Помимо передачи приборов в тестовые функции, вы также можете передавать приборы в другие приборы. Таким образом, вы можете построить сложные процессы настройки тестирования с минимальным повторением кода.
В качестве примера давайте создадим приспособление для распределения токенов, которое требует нашего исходного приспособления для токенов:
Затем мы можем добавить distribute_tokens
к любому тесту, где нам требуется начальный баланс токенов во многих учетных записях.
Встроенные светильники
Brownie предоставляет нам набор встроенных приспособлений для доступа к различным функциям. В качестве примера давайте продолжим рефакторинг наших текущих тестов, чтобы использовать встроенные accounts
и Token
фикстуры:
Нам удалось удалить оператор import
, вместо этого получив доступ к той же функции через встроенные фикстуры.
Большинство основных компонентов Brownie и объектов проекта доступны через приспособления - Документация Brownie содержит полный список.
Объем приспособления
По умолчанию прибор должен выполняться каждый раз, когда он требуется для проверки. Добавляя параметр scope
в декоратор, мы можем изменить частоту выполнения фикстуры:
Установив наш прибор в область видимости модуля, он теперь разворачивает только один Token
контракт, передавая одно и то же значение в оба теста. С помощью сложных наборов тестов это может сэкономить вам значительное количество времени.
Приспособления с более высокими областями видимости (например, session
или module
) всегда создаются перед приборами с более низкими областями видимости (например, function
). Порядок выполнения фикстур одной и той же области видимости определяется порядком, в котором они объявлены в качестве входных аргументов фикстуры и теста. Единственное исключение из этого правила - изолирующие приспособления, которые мы рассмотрим далее.
Тестовая изоляция
Как мы обсуждали ранее, изоляция - ключевой компонент хорошо написанного набора тестов. Практически во всех случаях вы должны изолировать свои тесты друг от друга, чтобы действия, выполняемые в одном тесте, не могли повлиять на результат тестов, следующих за ним.
Brownie предоставляет два приспособления, которые используются для изоляции:
module_isolation
- это приспособление с ограниченной областью видимости. Он сбрасывает локальную цепочку до и после завершения тестового модуля, обеспечивая чистую среду для модуля и то, что его результаты не повлияют на последующие модули.fn_isolation
- это функция в области видимости. Кроме того, он делает снимок цепочки перед запуском каждого теста и возвращается к нему после завершения теста. Это приспособление позволяет вам определять общее состояние цепочки для каждого теста, сокращая повторяющиеся транзакции и тем самым ускоряя выполнение ваших тестов.
Изолирующие приспособления всегда выполняются в первую очередь. Вы можете быть уверены, что все фикстуры в области модуля будут выполнены до моментального снимка изоляции, а все фикстуры в области функций будут запущены после моментального снимка.
Чтобы применить прибор изоляции ко всем тестам в модуле, потребуйте его в другом приспособлении и включите аргумент ключевого слова autouse
:
Собираем все вместе
Давайте проведем последний рефакторинг наших тестов, объединив все, что мы обсуждали до сих пор:
- В строках 5–7 мы объявляем приспособление, которое разворачивает
Token
контракт. Мы установили его как модуль с областью видимости, чтобы он разворачивал только один контракт для всех тестов. - В строках 10–12 мы добавляем приспособление
fn_isolation
, чтобы гарантировать, что наши тесты должным образом изолированы. Снимок локальной цепочки блоков будет сделан сразу после выполненияtoken
фикстуры, и каждый тест будет запускаться с этого снимка. - Строки 15–19 - это наш первый тест. Мы используем встроенный прибор
accounts
, а также приборtoken
, который мы объявили ранее. В этом тесте мы переводим несколько токенов и делаем утверждение о балансе отправителя. - Строки 22–26 - это наш второй тест. Очень похоже на предыдущий тест, за исключением того, что на этот раз мы делаем утверждение о балансе приемника.
- Строки 29–31 - это наш третий и последний тест. В этом тесте мы используем
brownie.reverts
диспетчер контекста, чтобы подтвердить возврат транзакции, когда мы пытаемся передать слишком много токенов.
Сохраните тесты как tests/test_first.py
внутри проекта токена. Затем запустите их с помощью следующей команды:
brownie test tests/test_first.py
Вы должны получить примерно такой результат:
Каждая зеленая точка представляет собой один из наших тестов. Все прошло! 🎉
Что дальше
В разделе Часть четвертая: Выполнение ваших тестов мы узнаем все тонкости запуска нашего набора тестов, понимания результатов и отладки неудавшихся тестов.
Вы также можете подписаться на Twitter account Brownie, прочитать другие мои Medium статьи и присоединиться к нам в Gitter.