Обратите внимание, что я не связан с R3.

Это руководство объяснит и предоставит примеры того, как писать модульные тесты для контрактов в Corda с использованием Java и IntelliJ. Тестирование децентрализованных приложений особенно важно, потому что без централизованного управления будет сложнее внедрять исправления или контролировать исправления в случае непредвиденных последствий из-за ошибок или лазеек в системе безопасности. При этом будет критически важно убедиться, что все ваши пункты кода контракта протестированы и работают должным образом, прежде чем развертывать распределенное приложение в производственной среде.

Примечание. В этом руководстве мы будем использовать Java, но те же принципы применимы и к Kotlin, если вы так предпочитаете. Базовое знакомство с Java должно упростить перевод этого кода на Kotlin.

Начиная

Если вы еще этого не сделали, настройте среду разработки, а затем настройте проект Corda в IntelliJ, клонировав Проект шаблона Java из GitHub, который устанавливает базовую файловую структуру, но не имеет реальной функциональности.

Краткий обзор концепций

Если вы новичок в Corda, вам обязательно стоит просмотреть Corda docs, чтобы получить базовое концептуальное представление о ключевых концепциях. Однако мы быстро определим ключевые концепции для этого урока:

  • Состояния - общие факты, по которым узлы Corda достигают консенсуса и затем сохраняются в реестре.
  • Контракты - набор правил, которые налагают ограничения на то, как состояния могут быть созданы в реестре и как они могут развиваться.
  • Потоки - инкапсулируют процедуру выполнения определенного обновления бухгалтерской книги.
  • Команды - предоставляется вместе с предлагаемым обновлением бухгалтерской книги, чтобы указать намерение транзакции; влияет на то, какие правила проверки применяются.

Файловая структура

Для наших целей на данный момент нас действительно интересуют только 3 каталога:

  1. cordapp / src / main /… (Сюда пойдут потоки - и многое другое.)
  2. cordapp / src / test /… (Здесь находятся модульные тесты. 👍 💯)
  3. cordapp-контракты-состояния /… (Контракты и состояния находятся здесь.)

Техническое примечание. В файловой структуре шаблона есть два отдельных пакета (cordapp и cordapp-contract-states). Это отделяет объекты Contract и State от Flows и остальных классов приложения - причина в том, что объекты Contract и State необходимо передавать по сети, поэтому, поместив их в отдельный пакет, мы можем избежать потери полосы пропускания.

В шаблоне проекта есть заполнители State, Contract и Flow - давайте начнем с них и внесем несколько изменений для начала:

TemplateState.java

cordapp-contract-states / src / main / java / [пакет]

Вот TemplateState как есть из репо:

Мы собираемся создать тривиальный пример состояния для хранения обновлений бухгалтерской книги о балансе банковского счета человека. Давайте заменим содержимое этого класса следующим:

Совет. Вы можете автоматически сгенерировать методы получения в IntelliJ, щелкнув объявление экземпляра правой кнопкой мыши и выбрав Создать - ›Получатели и выбрав все свои переменные.

TemplateContract.java

cordapp-contract-states / src / main / java / [пакет]

Вот TemplateContract как есть из репо:

Давайте добавим в метод verify () следующее:

Мы тут:

  1. Извлеките команду из транзакции. Если его нет, то метод requireSingleCommand () автоматически создает исключение.
  2. Проверьте, принадлежит ли команда к одному из определенных нами типов команд. В данном случае у нас есть только один тип: Action. Если это не этот тип, то вызовите исключение.

А как насчет модульных тестов !?

Теперь мы готовы приступить к внедрению TDD и разработке наших контрактных спецификаций.

В проекте Template уже есть конфигурация запуска IntelliJ, настроенная для выполнения тестов Contract, Flow и Integration. Прямо сейчас у нас есть фиктивный тест контракта, определенный в ContractTests.java (cordapp / src / test / ..),, поэтому давайте попробуем его:

И мы должны увидеть:

Подготовка к модульному тесту

Corda имеет встроенный интерфейс MockServices, который позволяет очень легко писать тесты контрактов. Используя метод «бухгалтерской книги», наши имитирующие сервисы будут предоставлять чистый лист для каждого теста без каких-либо ранее существовавших транзакций или услуг. Таким образом, мы можем быть уверены, что каждый тест изолирован и не имеет побочных эффектов.

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

ContractTests.java

cordapp / src / test / java / [пакет]

Как использовать MockServices

Давайте обсудим использование метода ledger (). См. ниже:

(Изменить: если IntelliJ не может автоматически импортировать реестр, вы можете вручную добавить его с помощью «import static net.corda.testing.node.NodeTestUtils.ledger;»).

  1. Мы вызываем бухгалтерскую книгу, предоставляя наш экземпляр объекта MockServices, который мы назвали ledgerServices.
  2. Затем у нас есть выражение-предикат ledger -> {}. Все, что находится внутри этих скобок, будет в нашей изолированной пустой бухгалтерской книге.
  3. В нашем выражении бухгалтерской книги мы создадим новую транзакцию, используя метод ledger.transaction(tx -> {});. Все, что находится внутри этого второго набора скобок, будет соответствовать новой транзакции, на которую ссылается переменная tx.
  4. На данный момент нас не интересуют возвращаемые значения, поэтому мы вернем null из обоих выражений.

Добавить модульный тест

А теперь давайте избавимся от этого dummyTest и заменим его чем-нибудь легитимным.

Предположим, что на данный момент бухгалтерская книга поддерживает только добавление банковского счета и записи баланса, но не поддерживает их изменение или удаление. В этом случае наши транзакции создадут одно состояние вывода - запись TemplateState, которая содержит номер счета и баланс - и не потребляет НИКАКИХ состояний ввода. Таким образом, нашему контракту необходимо будет убедиться, что транзакция не включает состояние ввода, если она должна быть утверждена как действительная.

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

Запустим модульный тест!

А также…

1 test failed. 😢 😢 😢

Uh oh..

Шучу, это то, что мы хотим, потому что мы не написали никакого кода в нашем классе Contract, чтобы генерировать исключение, которое проверяет тест!

java.lang.AssertionError: Expected exception but didn’t get one👍

Это философия TDD. Сначала тестируйте, затем код. Мы должны пройти этот тест, не внося никаких изменений в сам тест.

Примечание. Если вы получаете сообщение об ошибке «Параметр конструктора -‘ arg0 ’- не относится к свойству…», ничего страшного! Вам просто нужно добавить следующие строки в ваш .idea/compiler.xml файл:

<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
</component>

Если у вас все еще есть проблемы, попробуйте перестроить, очистить градиент, перезапустить IntelliJ и т. Д., Чтобы убедиться, что ничего не кэшируется.

Написать код контракта

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

Используя метод requireThat (), мы можем иметь любое количество предложений require.using(), каждое из которых принимает строку, которая будет использоваться для сообщения об исключении, и выражение, которое вызовет исключение, если оно будет оценено как истинное. Здесь мы берем список входных данных из нашей транзакции, вызывая getInputs () для нашей введенной переменной транзакции tx,, а затем проверяем, пуст ли он.

Теперь, когда мы повторно запускаем наш модульный тест ...

Прошло! 👍 💯 😍

Продолжить цикл = ›написать неудачный тест, сделать его пройденным

Давайте добавим еще несколько модульных тестов контракта для хорошей оценки:

Запускаем тесты ..

😨

Выглядит устрашающе, но к этому привыкнешь. 😅

Новые тесты терпят неудачу. Добавьте условия контракта, чтобы они прошли:

Обратите внимание, что в Строке 8 мы получаем первое (только любое) состояние вывода из нашего объекта транзакции tx и преобразуем его в TemplateState, чтобы мы можете проверить его свойства.

И повторный запуск тестов…

Сейчас все проходит!

💯 👍 😄

Хорошо ... Итак, тесты проходят, но как я могу узнать, что все действительно безопасно? Я имею в виду, что если я добавил правило кода контракта и забыл добавить модульный тест. Нужны ли мне модульные тесты для моих модульных тестов !?

Нет, это было бы смешно. Но я понимаю вашу точку зрения. Откуда вы знаете, что ваши тесты действительно охватывают все основы? Используя инструмент Охват кода, поставляемый с IntelliJ, мы можем выяснить.

🙋 Простите, профессор Бренден, у меня вопрос.

👴 Что случилось, Сандра?

👩 Похоже, вы просто учите нас использовать IntelliJ, который я уже знаю. Какая разница? Я просто хочу создать децентрализованную книгу банковских счетов для личного пользования.

👴 Верно. И если вы уже знаете о покрытии кода, извините, что утомил вас. Я хочу подчеркнуть, что для приложений блокчейна критически важно, чтобы все правила проверки были проверены должным образом, потому что без централизованного управления гораздо труднее выпускать исправления и исправления!

💁 Скучно, я пропущу вперед!

👴 😢 ОК ... Перейдите к разделу Написание дополнительных тестов для контрактов.

Покрытие кода!

IntelliJ имеет встроенное покрытие кода, и это очень просто. Единственная загвоздка в том, что нам придется немного переформатировать код нашего контракта, чтобы сделать возможным анализ покрытия для наших правил проверки. Каким бы элегантным ни был метод requireThat, мы собираемся преобразовать его в старые добрые операторы if:

Этот код идентичен тому, что у нас был раньше, но он позволит нам увидеть, оцениваются ли строки «throw new IllegalArgumentException» или нет. Если это так, мы знаем, что у нас есть модульный тест, который вызывает это исключение; если нет, то мы знаем, что код никогда не достигается и что нам не хватает теста.

Итак, давайте проверим это. Сначала давайте закомментируем один из наших модульных тестов, чтобы мы могли позволить Code Coverage предупредить нас об его отсутствии.

Теперь убедитесь, что покрытие кода включено, перейдя в «Редактировать конфигурации запуска», выбрав «Выполнить тесты контракта» и щелкнув вкладку «Покрытие кода». Убедитесь, что выбран один из вариантов; либо будет работать.

Посмотрите на крайний левый угол рядом с номерами строк. Вы увидите, что рядом с каждой строкой «throw new ...» мы видим зеленый прямоугольник, за исключением строки 39, где мы видим красный. Красный цвет означает, что линия не была достигнута, и поэтому мы знаем, что исключение для «1 обязательного подписывающего» никогда не создавалось - это означает, что для него нет проверки!

Если мы не комментируем этот тест, то вуаля! 💚

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

Написание более сложных тестов для контрактов

Вот обзор того, как можно создавать более сложные тесты.

MockServices

Для некоторых функций вы можете захотеть предоставить параметры для MockServices. Это может включать в себя предоставление списка пакетов cordapp для сканирования и, например, внедрение TestIdentity и IdentityService в объект MockServices. По умолчанию он просто будет использовать фиктивный TestIdentity при создании экземпляра без параметров.

Методы испытаний

Методы построения транзакций модульного тестирования:

  • tx.inputs () - Добавить входы
  • tx.outputs () - Добавить выходы
  • tx.attachments () - Добавить вложения с помощью SecureHash
  • tx.command () - Добавить команды
  • tx.timeWindow () - Добавить TimeWindows
  • tx.tweak () - Создать изолированную копию транзакции
  • tx.verify () - Контракт проверяет утверждение
  • tx.fails () - Проверка контракта вызывает исключение
  • tx.failsWith () - проверка контракта вызывает исключение с заданным сообщением

Способы ведения бухгалтерской книги с использованием MockServices:

  • l.transaction () - Подтвержденная транзакция
  • l.unverifiedTransaction () - Непроверенная транзакция
  • l.attachment () - Загрузить вложение
  • l.tweak () - Создать изолированную копию бухгалтерской книги

твик () 💊

Ладно, не Meth, но что-нибудь более безопасное! Это можно использовать либо на уровне бухгалтерской книги, либо на уровне транзакции для создания локальной изолированной копии (бухгалтерской книги или транзакции). Вы можете изменять и утверждать копию, не затрагивая оригинал. Это используется для того, чтобы вы могли иметь несколько утверждений fails () в одном тесте, проверяя различные состояния ошибок.

Шпаргалка

Замечания

Спасибо за чтение - надеюсь, это было полезно!

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

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

Ваше здоровье! 💻 😄

использованная литература

Https://docs.corda.net/key-concepts.html

Https://docs.corda.net/tutorial-test-dsl.html

Https://github.com/corda/obligation-cordapp

Https://stackoverflow.com/questions/48772259/java-io-notserializableexception-constructor-parameter-arg0-doesnt-refer-to-a

Ресурсы

Документация Corda - Примеры проектов Corda - Corda Slack Group