Едно просто ръководство за това как да пишете тестове на единици за интелигентни договори

http://upstate.agency/smart-contracts

Добрите тестове на модула са от решаващо значение за разработването на интелигентни договори. В действителност, предвид интелигентните договори са неизменни и често са отговорни за управлението на големи суми пари, може да се твърди, че разработването на добри тестове за интелигентни договори е по-важно от традиционните уеб и мобилни приложения.

През последните два месеца бяхме изключително щастливи да работим в тясно сътрудничество с компания за финансови услуги, която разработва иновативна платформа за токенизиране на ценни книжа по регулаторен начин. Тяхната скоро пусната платформа ще въведе възможността за преход на милиарди долари ценни книжа към блокчейна.

В подкрепа на стартирането им, ние работихме в тясно сътрудничество с техния екип за инженеринг и внедрихме цялостно покритие за тестове и изпълнихме спрямо нашия процес на откриване на грешки. От опитът се отдалечихме с няколко основни съображения, както и няколко съвета и трика, които можете да използвате, когато разработвате тестови единици за вашите интелигентни договори.

Получавайте здравословно разбиране на бизнес логиката

Първото нещо, което трябва да направите, е да се уверите, че имате задълбочено разбиране на бизнес логиката (dapp) на разпределеното ви приложение. Полезно е да документирате проблемите, които ще разреши Dapp, и как ще ги разрешите. Също така ще искате да идентифицирате различните ограничения, които вашият дап има. Например, повечето dapps въвеждат някаква форма на контрол на достъпа, базиран на роли или базиран на договор контрол. Важно е да се документират тези ограничения и въздействието, което те ще имат върху използването.

Не само това, вие също ще искате да препишете работния процес на вашия дап. Необходимо ли е да бъдат активирани определени договори преди други? Трябва ли даден пауза да бъде спряна или прекратена, за да се изпълни определена задача? Това са типовете изисквания за бизнес логика и свързани с работния процес, които трябва да бъдат добре разбрани, преди да се потопите и да започнете да разработвате своите тестове за единица.

Съставете тестовете на устройството си

След като сте документирали бизнес логиката на вашия дапп и работния процес, препоръчваме ви да очертаете всеки тест на единица, свързан със съответната функционалност на вашия дап. Това изисква да разрушите дапп, договор по договор и функция по функция, и в крайна сметка да посочите всички съответни тестове на единица, свързани с всеки потенциален случай на използване.

За целта групираме нашите тестове на единици заедно, така че те да бъдат картографирани към съответните функции в договорите. Ето примерна спецификация за случай на еднократна употреба, свързан с функция buyTokens (_amount) в договор на Token:

// когато договорът е активиран
   // когато договорът не е поставен на пауза
      // когато е започнал периодът на предлагане
         // когато периодът на предлагане не е приключил
            // трябва да върне вярно

Обратно, ето още една спецификация, свързана с функцията buyTokens (_amount), когато периодът на предлагане приключи:

// когато договорът е активиран
   // когато договорът не е поставен на пауза
      // когато е започнал периодът на предлагане
         // когато периодът на предлагане приключи
             // трябва да се върне

Оформянето на всички ваши тестове на единици като този е добър начин да улесните разговорите със своите заинтересовани страни и останалата част от вашия инженерен екип. Помага да се гарантира, че всички са на една и съща страница, когато става дума за изискванията на вашия дап. Не само това, той ви помага да се ориентирате как да архитектирате и изградите вашия Dapp по сигурен и надежден начин.

Първо се обърнете към модификаторите, след това преработете вашата заявка и ако операторите последователно

След като очертаете тестовете на устройството си, препоръчваме ви първо да се съсредоточите върху адресирането на случаите на употреба, свързани с модификаторите на вашите функции. След това можете да преминете през функцията последователно и да разработите тестове на единици, за да адресирате всички случаи на използване, свързани с всяко изискване и ако оператор.

Например, за функция като следната, първо искаме да разгледаме случаите на използване, свързани с модификаторите atStage (Stages.AuctionStarted) и validBid (), преди да се обърнем към изразите if и else, които следват:

/// @dev Първият, който предостави оферта в currentAskingPrice, се възлага на NFT на бенефициента
/// @dev Ако даден участник наддаде на NFT, той ще спечели търга и надценката им ще бъде върната функция
processBid ()
обществен
платим
atStage (Stages.AuctionStarted) // Започнете първо тук
validBid () // адресирайте тези случаи на използване на второ място
връща (uint) // След това адресирайте изявленията „ако“, „друго“ и „изисквам“, които следват
{
  оферта = msg. стойност;
  bidder = msg.sender;
  
  getCurrentAskingPrice ();
  ако (оферта> currentAskingPrice)
  {
    overrage = bid.sub (currentAskingPrice);
    bidder.transfer (излишък);
    bid = currentAskingPrice;
    етап = Stages.AuctionEnded;
    payBeneficiary ();
    sendToBidder ();
  }
  иначе ако (bid == currentAskingPrice)
  {
    етап = Stages.AuctionEnded;
    payBeneficiary ();
    sendToBidder ();
  }
  още
  {
    revert („Офертата е по-ниска от currentAskingPrice“);
  }
}

За тестване кога договорите ви трябва да се отменят, намерихме помощника на OpenZeppelin на assrtRevert за изключително полезен. Можете да го използвате така:

изчакайте assertRevert (tokenInstance.buy ({от: инвеститор, стойност: 500}));

Функционално претоварване и необходимост от ниско ниво повиквания

Докато продължавате да разработвате тестовете на устройството си, вероятно ще срещнете недостатъци, свързани с инструментите за програмисти, които използвате. Това е така, защото пространството за разработка на интелигентни договори все още е много ново и следователно много от инструментите за разработчици там все още са незрели.

Например, рамката на Truffle - която е отличен инструмент и може би най-популярната рамка в пространството на Ethereum днес - не поддържа претоварване на функции. Тоест, ако трябва да тествате две функции с едно и също име, но различни артерии, ще трябва да използвате ниско ниво повиквания, за да тествате втората функция в ABI на договора. Ако не го направите, ще получите Грешка: Невалиден брой аргументи за функцията Solidity. Нека да разгледаме един бърз пример.

Ако имате две функции buyTokens в договор, една, която не взема аргументи и е посочена на първо място в ABI и тази, която приема (_amount) аргумент и е посочена на второ място в ABI, ще трябва да използвате ниско ниво на повикване за да тествате функцията buyTokens (_amount) с помощта на encodeCall, както се вижда по-долу.

data = encodeCall ("buyTokens", ['uint'], [100]); // 100 е стойността на аргумента `_amount`
contractInstance.sendTransaction ({данни, от: инвеститор, стойност: 500})

Общността на трюфелите е запозната с този проблем и той ще бъде разгледан в следващо издание. Въпреки това, странни сценарии като този не са нетипични, когато разработвате интелигентни договори и техните съответни тестови единици. Ще трябва да получите хитър (но все пак да бъдете изключително внимателни) с решенията, на които се позовавате от време на време.

Как да тествате вътрешни функции

Дадените функции в Solidity могат да имат различни видимости (т.е. публични, частни, вътрешни и външни), заслужава да се отбележи, че разработването на тестовете за вътрешни функции е малко по-различно от разработването им за обществени функции. Това е така, защото вътрешните функции не са изброени в ABI на интелигентен договор, след като е бил съставен. Следователно, за да тествате вътрешни функции, имате две възможности:

  1. Можете да създадете друг договор, който наследява договора, който тествате, за да тествате вътрешна функция
  2. Или можете да тествате логиката на вътрешна функция от другите функции в договора, които извикват вътрешната функция

Всеки от двата подхода ще свърши работата, макар че някои може да твърдят, че тестването на логиката на вътрешна функция от другите функции в договора е по-интуитивно.

Handy Ganache съвети и трикове

Както споменах по-рано, ние използваме предимно рамката на Truffle, за да изградим дапсове за нашите клиенти. Трюфелът използва инструмент, наречен Ganache, който ви позволява бързо да задействате собствения си личен блокчейн Ethereum, който можете да използвате за стартиране на тестове, изпълнение на команди и проверка на състоянието, докато контролирате как работи веригата. Много е удобно.

Това, което наистина е приятно за Ganache, е как той лесно може да бъде персонализиран, за да задоволи уникалните нужди на вашия дап. Например, ние разработихме единични тестове за dapps, които изискват от нас да тестваме случаи на използване за десетки и десетки потребители. С една проста команда Ganache ни улеснява да тестваме с колкото се може повече акаунти:

ganache-cli -a 40 // Където флагът a a означава стартиране на Ganache с 40 тестови акаунта

Освен това можем да настроим балансите на тези сметки да започват с толкова ETH, колкото е необходимо за нашите тестове. По подразбиране Truffle задава балансите на 100 ETH. Лесно можем да увеличим или намалим тази стойност в зависимост от нашите собствени уникални изисквания:

ganache -e 10000` // Където флагът e означава колко ETH всеки акаунт трябва да започва по подразбиране

Ganache е изключително полезен инструмент, когато става въпрос за разработване на интелигентни договори за Ethereum. Той помага за оптимизиране на развитието, намаляване на риска и подобряване на надеждността на dapp.

Обвързване на всичко заедно

Тестването на единици в пространството за интелигентни договори е по принцип подобно на тестването на единици в света на уеб и мобилните приложения. Най-голямата разлика е, че интелигентните договори днес са неизменни и обикновено не предлагат прости начини да ги повторите във времето [1]. Следователно разработването на добри тестове за единица е от изключително значение предвид интелигентната неизменност на договорите и факта, че те често са отговорни за управлението на големи суми пари. Единственият начин да бъдете сигурни, че вашите интелигентни договори са сигурни и надеждни, е да ги тествате внимателно.

За да се осъществи масовото приемане на интелигентни договори, от решаващо значение е нашата общност да работи заедно за увеличаване на пространството и да улесни разработчиците да пишат сложни приложения върху публичните блокчейн.

Надяваме се, че тези уроци, извлечени от нашия опит с разработването на тестове за единица, са полезни и подкрепят вашите усилия за изграждане на сигурни и надеждни интелигентни договори. Моля, не се колебайте да се свържете, ако имате допълнителни въпроси или мисли, които искате да обсъдите допълнително с нашия екип - [email protected]

В близките дни ще публикуваме последваща статия към това, Какво научих от тестване на сложни интелигентни договори.

[1] Екипът на Zeppelin наскоро пусна ZeppelinOS, който предоставя на веригата набор от надграждащи се стандартни библиотеки и прави възможно надграждането на вашите интелигентни договори, използвайки прокси модели.

☞ За да сте в крак с работата ни с разработването на интелигентни договори, следвайте ни в Medium и Twitter.