Přeskočit na hlavní obsah

Testování jednoduchého chytrého kontraktu s knihovnou Waffle

smart kontrakt účty
solidity
Waffle
testování
Začátečník
Ewa Kowalska
26. února 2021
5 minuta čtení

V tomto návodu se naučíte, jak

  • Testovat změny zůstatku peněženky
  • Testovat emisi událostí se zadanými argumenty
  • Ověřit, že byla transakce vrácena

Předpoklady

  • Můžete vytvořit nový projekt v JavaScriptu nebo TypeScriptu
  • Máte nějaké základní zkušenosti s testy v JavaScriptu
  • Použili jste nějaké správce balíčků jako yarn nebo npm
  • Máte velmi základní znalosti chytrých kontraktů a Solidity

Začínáme

Tento návod ukazuje nastavení a spuštění testu pomocí yarn, ale není problém, pokud dáváte přednost npm – poskytnu správné odkazy na oficiální dokumentaciopens in a new tab Waffle.

Instalace závislostí

Přidejteopens in a new tab závislosti ethereum-waffle a typescript do vývojářských závislostí vašeho projektu.

yarn add --dev ethereum-waffle ts-node typescript @types/jest

Příklad chytrého kontraktu

Během tohoto návodu budeme pracovat na jednoduchém příkladu chytrého kontraktu – EtherSplitter. Nedělá toho moc kromě toho, že umožňuje komukoli poslat nějaké wei a rovnoměrně je rozdělit mezi dva předdefinované příjemce. Funkce split vyžaduje, aby byl počet wei sudý, jinak se vrátí zpět. Pro oba příjemce provádí převod wei následovaný emisí události Transfer.

Vložte úryvek kódu EtherSplitter do src/EtherSplitter.sol.

1pragma solidity ^0.6.0;
2
3contract EtherSplitter {
4 address payable receiver1;
5 address payable receiver2;
6
7 event Transfer(address from, address to, uint256 amount);
8
9 constructor(address payable _address1, address payable _address2) public {
10 receiver1 = _address1;
11 receiver2 = _address2;
12 }
13
14 function split() public payable {
15 require(msg.value % 2 == 0, 'Lichá částka wei není povolena');
16 receiver1.transfer(msg.value / 2);
17 emit Transfer(msg.sender, receiver1, msg.value / 2);
18 receiver2.transfer(msg.value / 2);
19 emit Transfer(msg.sender, receiver2, msg.value / 2);
20 }
21}
Zobrazit vše

Kompilace kontraktu

Pro kompilaciopens in a new tab kontraktu přidejte následující položku do souboru package.json:

1"scripts": {
2 "build": "waffle"
3 }

Dále vytvořte konfigurační soubor Waffle v kořenovém adresáři projektu – waffle.json – a poté tam vložte následující konfiguraci:

1{
2 "compilerType": "solcjs",
3 "compilerVersion": "0.6.2",
4 "sourceDirectory": "./src",
5 "outputDirectory": "./build"
6}

Spusťte yarn build. Výsledkem je, že se objeví adresář build s kompilovaným kontraktem EtherSplitter ve formátu JSON.

Nastavení testu

Testování pomocí Waffle vyžaduje použití Chai matchers a Mocha, takže je musíte přidatopens in a new tab do svého projektu. Aktualizujte soubor package.json a přidejte položku test do části se skripty:

1"scripts": {
2 "build": "waffle",
3 "test": "export NODE_ENV=test && mocha -r ts-node/register 'test/**/*.test.ts'"
4 }

Chcete-li spustitopens in a new tab své testy, stačí spustit yarn test .

Testování

Nyní vytvořte adresář test a vytvořte nový soubor test\EtherSplitter.test.ts. Zkopírujte úryvek níže a vložte jej do našeho testovacího souboru.

1import { expect, use } from "chai"
2import { Contract } from "ethers"
3import { deployContract, MockProvider, solidity } from "ethereum-waffle"
4import EtherSplitter from "../build/EtherSplitter.json"
5
6use(solidity)
7
8describe("Rozdělovač etheru", () => {
9 const [sender, receiver1, receiver2] = new MockProvider().getWallets()
10 let splitter: Contract
11
12 beforeEach(async () => {
13 splitter = await deployContract(sender, EtherSplitter, [
14 receiver1.address,
15 receiver2.address,
16 ])
17 })
18
19 // sem přidejte testy
20})
Zobrazit vše

Pár slov na úvod. MockProvider poskytuje testovací verzi blockchainu. Poskytuje také testovací peněženky, které nám poslouží k testování kontraktu EtherSplitter. Můžeme získat až deset peněženek zavoláním metody getWallets() na providera. V příkladu získáme tři peněženky – pro odesílatele a pro dva příjemce.

Dále deklarujeme proměnnou nazvanou „splitter“ – to je náš testovací kontrakt EtherSplitter. Vytváří se před každým spuštěním jednotlivého testu metodou deployContract. Tato metoda simuluje nasazení kontraktu z peněženky předané jako první parametr (v našem případě peněženky odesílatele). Druhým parametrem je ABI a bytecode testovaného kontraktu – předáme tam soubor json zkompilovaného kontraktu EtherSplitter z adresáře build. Třetím parametrem je pole s argumenty konstruktoru kontraktu, což jsou v našem případě dvě adresy příjemců.

changeBalances

Nejprve zkontrolujeme, zda metoda split skutečně změní zůstatky v peněženkách příjemců. Pokud rozdělíme 50 wei z účtu odesílatele, očekávali bychom, že zůstatky obou příjemců se zvýší o 25 wei. Použijeme matcher changeBalances od Waffle:

1it("Změní zůstatky účtů", async () => {
2 await expect(() => splitter.split({ value: 50 })).to.changeBalances(
3 [receiver1, receiver2],
4 [25, 25]
5 )
6})

Jako první parametr matcheru předáváme pole peněženek příjemců a jako druhý – pole očekávaných navýšení na odpovídajících účtech. Pokud bychom chtěli zkontrolovat zůstatek jedné konkrétní peněženky, mohli bychom také použít matcher changeBalance, který nevyžaduje předávání polí, jako v příkladu níže:

1it("Změní zůstatek účtu", async () => {
2 await expect(() => splitter.split({ value: 50 })).to.changeBalance(
3 receiver1,
4 25
5 )
6})

Všimněte si, že v obou případech changeBalance a changeBalances předáváme funkci split jako callback, protože matcher potřebuje přístup ke stavu zůstatků před a po volání.

Dále otestujeme, zda byla po každém převodu wei emitována událost Transfer. Obrátíme se na další matcher od Waffle:

Emit

1it("Emituje událost při převodu na prvního příjemce", async () => {
2 await expect(splitter.split({ value: 50 }))
3 .to.emit(splitter, "Transfer")
4 .withArgs(sender.address, receiver1.address, 25)
5})
6
7it("Emituje událost při převodu na druhého příjemce", async () => {
8 await expect(splitter.split({ value: 50 }))
9 .to.emit(splitter, "Transfer")
10 .withArgs(sender.address, receiver2.address, 25)
11})
Zobrazit vše

Matcher emit nám umožňuje zkontrolovat, zda kontrakt emitoval událost při volání metody. Jako parametry matcheru emit poskytneme testovací kontrakt, u kterého předpokládáme emisi události, spolu s názvem této události. V našem případě je testovacím kontraktem splitter a název události – Transfer. Můžeme také ověřit přesné hodnoty argumentů, se kterými byla událost emitována – do matcheru withArgs předáme tolik argumentů, kolik naše deklarace události očekává. V případě kontraktu EtherSplitter předáváme adresy odesílatele a příjemce spolu s převedenou částkou wei.

revertedWith

Jako poslední příklad zkontrolujeme, zda byla transakce vrácena v případě lichého počtu wei. Použijeme matcher revertedWith:

1it("Vrátí zpět, když je částka wei lichá", async () => {
2 await expect(splitter.split({ value: 51 })).to.be.revertedWith(
3 "Lichá částka wei není povolena"
4 )
5})

Test, pokud projde, nás ujistí, že transakce byla skutečně vrácena. Musí však také existovat přesná shoda mezi zprávami, které jsme předali v příkazu require, a zprávou, kterou očekáváme v revertedWith. Pokud se vrátíme ke kódu kontraktu EtherSplitter, v příkazu require pro částku wei poskytujeme zprávu: ‚Lichá částka wei není povolena‘. Ta se shoduje se zprávou, kterou očekáváme v našem testu. Pokud by se neshodovaly, test by selhal.

Gratulujeme!

Udělali jste první velký krok k testování chytrých kontraktů s Waffle!

Stránka naposledy aktualizována: 14. února 2026

Byl tento tutoriál užitečný?