[Код этого сообщения в блоге можно найти здесь: https://github.com/gustavoguimaraes/smart-contract-testing-javascript-example-]
Разработчики смарт-контрактов стремятся уменьшить количество ошибок в своем коде, поскольку они могут быть в буквальном смысле дорогостоящими. Для этого одним из методов, которые мы используем, является подробное написание тестов или, я осмелюсь сказать, одержимость кодовой базой.
В этом посте я покажу вам, как начать тестирование смарт-контрактов с самого начала.
Во-первых, это зависимости, которые я использовал при создании этого руководства. Если вы столкнулись с ошибкой, вы можете попробовать установить и использовать эти версии.
- [email protected] - truffle@^4.0.0 - ethereumjs-testrpc@^4.0.1
Разобравшись с зависимостями, давайте настроим проект.
mkdir smart-contract-test-example && cd "$_" && truffle init webpack
Приведенный выше фрагмент создает каталог, переходит в него и инициализирует Truffle с помощью webpack.
Теперь создайте тестовый файл для FundRaise
смарт-контракта, который мы создадим.
touch test/fundRaise.js
Откройте файл в вашем любимом текстовом редакторе и добавьте самую простую структуру теста.
const FundRaise = artifacts.require('./FundRaise.sol') contract('FundRaise', function () { })
Первая строка получает артефакты контракта. Это абстракция контракта, содержащая его первичную информацию, то есть его ABI, адрес и тому подобное.
Затем мы создаем функцию contract()
, которая похожа на функцию describe()
в Mocha, за исключением того, что Truffle добавляет некоторые функции, такие как обеспечение развертывания контрактов перед запуском тестов. Кстати, Truffle использует под капотом фреймворк тестирования Mocha, а также библиотеку утверждений Chai.
Теперь давайте запустим этот простой тест.
Первый:
testrpc
Затем откройте новое окно командной строки и введите
truffle test test/fundRaise.js
Результат:
Error: Could not find artifacts for ./FundRaise.sol from any sources at Resolver.require (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:49072:9) at TestResolver.require (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:89733:30) ...
Это потому, что мы еще не написали смарт-контракт FundRaise
. Давай исправим это.
touch contracts/FundRaise.sol
Затем добавьте код
pragma solidity ^0.4.17; contract FundRaise { }
Снова запустите тест:
0 passing (1ms)
Верно. Еще нет написанных тестов.
Спецификация смарт-контракта и добавление тестов
Смарт-контракт FundRaise
будет простым контрактом. Его простая спецификация:
- у него есть владелец
- он принимает средства
- он может приостанавливать и возобновлять деятельность по сбору средств
- Владелец контракта может в любой момент снять средства с контракта себе.
Начнем с первой спецификации - у контракта есть собственник.
const FundRaise = artifacts.require('./FundRaise.sol') contract('FundRaise', function ([owner]) { let fundRaise beforeEach('setup contract for each test', async function () { fundRaise = await FundRaise.new(owner) }) it('has an owner', async function () { assert.equal(await fundRaise.owner(), owner) }) })
В приведенном выше тестовом коде мы делаем несколько вещей.
1- причудливое назначение деструктурирующей переменной ES2015 в function([owner]
первый параметр, заданный функции contract
, представляет собой массив с учетными записями, поступающими из testrpc. Берем первый и присваиваем ему переменную owner
.
2- создать переменную fundRaise
3- иметь функцию beforeEach
, которая будет запускаться перед каждым тестом, каждый раз создавая новый экземпляр fundRaise
. Обратите внимание на использование async/await
для обещаний. Это позволяет сделать тестовый код более читаемым. Если вы хотите узнать больше о новых async/await
функциях JavaScript, это хорошая запись в блоге.
4- создайте первый тест в функциональном блоке it()
. Здесь мы утверждаем, что fundRaise.owner()
- это owner
, который мы передали при создании контракта.
Перед повторным запуском тестов перейдите к truffle.js
и потребуйте babel-polyfill
, так как он нам нужен для использования async/await
.
truffle.js
// Allows us to use ES6 in our migrations and tests. require('babel-register') require('babel-polyfill') module.exports = { networks: { development: { host: 'localhost', port: 8545, network_id: '*' // Match any network id } } }
Запустите тесты еще раз, и вы обнаружите эту ошибку:
... 1 failing 1) Contract: FundRaise has an owner: AssertionError: expected undefined to equal '0x676c48fb3979cf2e47300e8ce80a99087589650d' ...
Пришло время написать код, который сделает первый тест пройденным. Давайте немного уточним наш смарт-контракт.
pragma solidity ^0.4.17; contract FundRaise { address public owner; // @dev constructor function. Sets contract owner function FundRaise() { owner = msg.sender; } }
Запустите тест снова, т.е.truffle test test/fundRaise.js
:
Contract: FundRaise ✓ has an owner (41ms) 1 passing (138ms)
Большой! Давайте продолжим и добавим следующий.
const FundRaise = artifacts.require('./FundRaise.sol') contract('FundRaise', function ([owner, donor]) { let fundRaise beforeEach('setup contract for each test', async function () { fundRaise = await FundRaise.new(owner) }) it('has an owner', async function () { assert.equal(await fundRaise.owner(), owner) }) it('is able to accept funds', async function () { await fundRaise.sendTransaction({ value: 1e+18, from: donor }) const fundRaiseAddress = await fundRaise.address assert.equal(web3.eth.getBalance(fundRaiseAddress).toNumber(), 1e+18) }) })
На этот раз ошибка:
1 failing 1) Contract: FundRaise is able to accept funds: Error: VM Exception while processing transaction: invalid opcode
Хорошо, нам нужно позволить нашему контракту получать эфир. Давай исправим это.
pragma solidity ^0.4.17; contract FundRaise { address public owner; // @dev constructor function. Sets contract owner function FundRaise() { owner = msg.sender; } // fallback function that allows contract to accept ETH function () payable {} }
И вот результат:
Contract: FundRaise ✓ has an owner (38ms) ✓ is able to accept funds (234ms) 2 passing (473ms)
Красивый. Это процесс, который нужно систематически выполнять, чтобы покрыть смарт-контракты тестами, написанными на JavaScript. Просто продолжайте этот процесс, пока не будут выполнены все спецификации смарт-контрактов.
Для простоты я собираюсь ускорить процесс и добавить полный набор тестов для FundRaise
contract, чтобы вы имели представление о том, как это будет выглядеть в итоге.
а вот полный код смарт-контракта:
Запустите тесты в последний раз…
Contract: FundRaise ✓ has an owner (46ms) ✓ accepts funds (193ms) ✓ is able to pause and unpause fund activity (436ms) ✓ permits owner to remove funds (653ms) 4 passing (2s)
Чудесный!
Надеюсь, вы кое-что узнали о тестировании смарт-контрактов с помощью JavaScript и его использовании при разработке блокчейнов. Теперь продолжайте и продолжайте тестирование своих смарт-контрактов.
Код для этого сообщения в блоге можно найти здесь: https://github.com/gustavoguimaraes/smart-contract-testing-javascript-example-