Přejít na hlavní obsah

Jak mockovat chytré kontrakty v Solidity pro testování

solidity
chytré kontrakty
testování
mockování
Středně pokročilý
Markus Waas
2. května 2020
3 minut čtení

Mock objekty (opens in a new tab) jsou běžným návrhovým vzorem v objektově orientovaném programování. Slovo pochází ze starofrancouzského „mocquer“, což znamená „dělat si legraci“, a postupně se vyvinulo ve význam „napodobovat něco skutečného“, což je přesně to, co děláme v programování. Dělejte si ze svých chytrých kontraktů legraci jen tehdy, když opravdu chcete, ale mockujte je, kdykoli můžete. Usnadní vám to život.

Unit testování kontraktů pomocí mocků

Mockování kontraktu v podstatě znamená vytvoření jeho druhé verze, která se chová velmi podobně jako ta původní, ale způsobem, který může vývojář snadno ovládat. Často skončíte u složitých kontraktů, kde chcete pouze unit testovat malé části kontraktu. Problém je v tom, co když testování této malé části vyžaduje velmi specifický stav kontraktu, do kterého je obtížné se dostat?

Pokaždé byste mohli psát složitou logiku pro nastavení testu, která kontrakt uvede do požadovaného stavu, nebo napíšete mock. Mockování kontraktu je snadné díky dědičnosti. Jednoduše vytvořte druhý mock kontrakt, který dědí z toho původního. Nyní můžete ve svém mocku přepisovat (override) funkce. Pojďme si to ukázat na příkladu.

Příklad: Privátní ERC-20

Použijeme ukázkový ERC-20 kontrakt, který má počáteční privátní období. Vlastník může spravovat privátní uživatele a pouze ti budou moci na začátku přijímat tokeny. Jakmile uplyne určitá doba, budou moci tokeny používat všichni. Pokud vás to zajímá, používáme hook _beforeTokenTransfer (opens in a new tab) z nových kontraktů OpenZeppelin v3.

A teď si ho namockujeme.

Dostanete jednu z následujících chybových zpráv:

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

Vzhledem k tomu, že používáme novou verzi Solidity 0.6, musíme přidat klíčové slovo virtual pro funkce, které lze přepsat, a override pro přepisující funkci. Pojďme je tedy přidat do obou funkcí isPublic.

Nyní můžete ve svých unit testech místo toho použít PrivateERC20Mock. Když chcete testovat chování během doby privátního používání, použijte setIsPublic(false) a podobně setIsPublic(true) pro testování doby veřejného používání. V našem příkladu bychom samozřejmě mohli k odpovídající změně časů použít také časové pomocníky (time helpers) (opens in a new tab). Myšlenka mockování by však nyní měla být jasná a jistě si dokážete představit scénáře, kde to není tak snadné jako pouhé posunutí času.

Mockování mnoha kontraktů

Může to začít být nepřehledné, pokud musíte pro každý jednotlivý mock vytvořit další kontrakt. Pokud vám to vadí, můžete se podívat na knihovnu MockContract (opens in a new tab). Umožňuje vám přepisovat a měnit chování kontraktů za běhu (on-the-fly). Funguje však pouze pro mockování volání jiného kontraktu, takže pro náš příklad by to nefungovalo.

Mockování může být ještě mocnější

Možnosti mockování tím nekončí.

  • Přidávání funkcí: Užitečné není jen přepisování konkrétní funkce, ale také pouhé přidávání dalších funkcí. Dobrým příkladem u tokenů je přidání funkce mint, která umožní jakémukoli uživateli získat nové tokeny zdarma.
  • Použití v testnetech: Když nasazujete a testujete své kontrakty na testnetech společně s vaší decentralizovanou aplikací (dapp), zvažte použití mockované verze. Vyhněte se přepisování funkcí, pokud opravdu nemusíte. Koneckonců chcete testovat skutečnou logiku. Ale přidání například funkce reset, která jednoduše resetuje stav kontraktu na začátek, může být užitečné, aniž by bylo nutné nové nasazení. Je zřejmé, že v kontraktu na Mainnetu byste něco takového mít nechtěli.