Перейти до основного вмісту

Як створювати моки смарт-контрактів Solidity для тестування

Solidity
смарт-контракти
тестування
моки
Середній рівень
Маркус Ваас
2 травня 2020 р.
4 хвилин на читання

Мок-об'єкти (Mock objects) (opens in a new tab) — це поширений шаблон проєктування в об'єктно-орієнтованому програмуванні. Походячи від старофранцузького слова «mocquer», що означає «насміхатися», воно еволюціонувало до «імітації чогось справжнього», що ми, власне, і робимо в програмуванні. Будь ласка, насміхайтеся зі своїх смарт-контрактів лише за власним бажанням, але створюйте їхні моки (імітації) за будь-якої нагоди. Це полегшить вам життя.

Модульне тестування контрактів за допомогою моків

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

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

Приклад: Приватний ERC-20

Ми використовуємо приклад контракту ERC-20, який має початковий приватний період. Власник може керувати приватними користувачами, і лише вони зможуть отримувати токени на початку. Коли мине певний час, використовувати токени зможуть усі. Якщо вам цікаво, ми використовуємо хук _beforeTokenTransfer (opens in a new tab) з нових контрактів ОупенЗеппелін версії 3.

А тепер створімо його мок.

Ви отримаєте одне з таких повідомлень про помилку:

  • PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.
  • PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.

Оскільки ми використовуємо нову версію Solidity 0.6, нам потрібно додати ключове слово virtual для функцій, які можна перевизначити, і override для функції, яка перевизначає. Тож додамо їх до обох функцій isPublic.

Тепер у ваших модульних тестах ви можете використовувати PrivateERC20Mock. Коли ви хочете протестувати поведінку під час приватного періоду використання, використовуйте setIsPublic(false), і так само setIsPublic(true) для тестування публічного періоду використання. Звісно, у нашому прикладі ми могли б просто використати допоміжні функції часу (time helpers) (opens in a new tab), щоб відповідно змінити час. Але ідея створення моків тепер має бути зрозумілою, і ви можете уявити сценарії, де все не так просто, як звичайне перемотування часу вперед.

Створення моків для багатьох контрактів

Процес може стати заплутаним, якщо вам доведеться створювати окремий контракт для кожного моку. Якщо це вас турбує, ви можете звернути увагу на бібліотеку MockContract (opens in a new tab). Вона дозволяє перевизначати та змінювати поведінку контрактів на льоту. Однак вона працює лише для створення моків викликів до іншого контракту, тому для нашого прикладу вона б не підійшла.

Моки можуть бути ще потужнішими

Можливості моків на цьому не закінчуються.

  • Додавання функцій: корисним є не лише перевизначення певної функції, але й просте додавання додаткових функцій. Хорошим прикладом для токенів є наявність додаткової функції mint, яка дозволяє будь-якому користувачеві безкоштовно отримувати нові токени.
  • Використання в тестових мережах: коли ви розгортаєте та тестуєте свої контракти в тестових мережах разом із вашим децентралізованим застосунком (dapp), розгляньте можливість використання версії з моками. Уникайте перевизначення функцій, якщо в цьому немає крайньої потреби. Зрештою, ви хочете протестувати реальну логіку. Але додавання, наприклад, функції скидання може бути корисним, оскільки вона просто скидає стан контракту до початкового, і нове розгортання не потрібне. Очевидно, ви б не хотіли мати таку функцію в контракті в Головній мережі.