
Die Bedingungen des Smart-Vertrags können nicht geändert werden. Wenn Sie einen intelligenten Vertrag erstellen, müssen Sie daher sicherstellen, dass er ordnungsgemäß funktioniert. Testen ist eine sichere Möglichkeit, einen Vertrag in verschiedenen Situationen zu testen. In diesem Tutorial erfahren Sie, welche Schritte Sie ausführen müssen.
Ich werde sagen:
- So bereiten Sie eine Testumgebung vor.
- So schreiben Sie JavaScript- Tests und führen sie in Truffle aus .
Das Tutorial richtet sich an Anfänger, die gerade erst begonnen haben, sich mit der Entwicklung und dem Testen intelligenter Verträge auf Ethereum zu befassen. Um dieses Tutorial zu verstehen, müssen Sie mindestens Grundkenntnisse in
JavaScript und
Solidity haben .
1. So bereiten Sie eine Testumgebung vor
Es gibt viele Möglichkeiten, intelligente Verträge zu testen, aber
Truffle ist das beliebteste Testschreibwerkzeug mit
JavaScript . Auf
Truffle können Sie Komponententests schreiben und vollständige Integrationstests mit realen Parametern aus der Produktionsumgebung durchführen.
Zuerst müssen Sie die neueste Version von Node.js von der
offiziellen Website installieren.
Öffnen Sie dann ein Terminal und installieren Sie
Truffle mit dem folgenden Befehl:
npm install -g truffle
Erstellen Sie nach der Installation von
Truffle , ohne das Terminal zu schließen, das
Finanzierungsverzeichnis :
mkdir Funding
Wechseln Sie als Nächstes mit dem folgenden Befehl in das Verzeichnis:
cd Funding
Führen Sie den folgenden Befehl aus, um das Verzeichnis zu initialisieren:
truffle init
Nach Ausführung des Befehls werden die folgenden Ordner und Dateien im
Finanzierungsverzeichnis erstellt :

Weiter werden wir mit jedem Verzeichnis arbeiten. In der Zwischenzeit bereiten wir die Testumgebung weiter vor.
Zum Ausführen der Tests benötigen Sie Javascript-Bibliotheken.
Mocha ist eine Bibliothek, die allgemeine Funktionen zum Testen enthält, einschließlich
beschreiben und
es .
Chai ist eine Bibliothek, die eine Vielzahl von Funktionen für Überprüfungen unterstützt. Es gibt verschiedene „Stile“ zum Überprüfen der Ergebnisse, und Chai bietet uns diese Möglichkeit. Im Tutorial werden wir
Sollte verwenden .
Standardmäßig ist Mokka Teil von Truffle. Wir können die Bibliotheksfunktionen problemlos verwenden. Chai muss manuell installiert werden. Verwenden Sie dazu das Terminal und führen Sie im Stammverzeichnis des Projekts den folgenden Befehl aus:
npm install chai
Ich habe auch die
Chai-Bignumber-Bibliothek installiert, um Zahlen mit beliebiger Genauigkeit zu vergleichen:
npm install --save-dev chai-bignumber
Die Testumgebung ist dafür bereit. Jetzt können Sie beginnen, einen intelligenten Vertrag zu entwickeln und ihn zu testen.
2. Wie schreibe ich JavaScript-Tests und führe sie in Truffle aus?
Um Tests zu entwickeln, benötigen Sie einen intelligenten Vertrag. Wir werden einen Vertrag entwickeln, der es Ihnen ermöglicht, Spenden zu sammeln, einen bestimmten Betrag festzulegen, um die Sammlung zu erreichen, und Geld abzuheben. Wenn jemand mehr spendet, wird ihm die Differenz zwischen dem angesammelten Betrag und dem Betrag, der eingezogen werden muss, zurückerstattet.
Gehen Sie zum Verzeichnis
Finanzierung -> Verträge . Erstellen
Sie unter Finanzierung / Verträge die Datei
Funding.sol mit der Erweiterung
.sol. Dies ist der intelligente
Testvertrag .
Fügen Sie in
Funding.sol den folgenden Code hinzu:
pragma solidity 0.4.24; contract Funding { uint public raised; uint public goal; address public owner; event Donated(uint donation); event Withdrew(uint amount); modifier onlyOwner() { require(owner == msg.sender); _; } modifier isFinished() { require(isFunded()); _; } modifier notFinished() { require(!isFunded()); _; } constructor (uint _goal) public { owner = msg.sender; goal = _goal; } function isFunded() public view returns (bool) { return raised >= goal; } function donate() public payable notFinished { uint refund; raised += msg.value; if (raised > goal) { refund = raised - goal; raised -= refund; msg.sender.transfer(refund); } emit Donated(msg.value); } function withdraw() public onlyOwner isFinished { uint amount = address(this).balance; owner.transfer(amount); emit Withdrew(amount); } }
Der Vertrag ist fertig. Wir werden einen intelligenten Vertrag durch Migration bereitstellen.
Migrationen sind
JavaScript- Dateien, mit denen Sie Verträge im Ethereum-Netzwerk bereitstellen können. Dies ist die Hauptmethode zum Bereitstellen von Verträgen.
Wechseln Sie in das Verzeichnis
Finanzierung -> Migrationen, erstellen Sie die Datei
2_funding.js und fügen Sie den folgenden Code hinzu:
const Funding = artifacts.require("./Funding.sol"); const ETHERS = 10**18; const GOAL = 20 * ETHERS; module.exports = function(deployer) { deployer.deploy(Funding, GOAL); };
Um die Tests auszuführen, müssen Sie den Befehl
Trüffeltest verwenden . Wechseln Sie im Terminal zum Stammverzeichnis des
Finanzierungsverzeichnisses , das während der Vorbereitung der Testumgebung erstellt wurde, und geben Sie Folgendes ein:
truffle test
Wenn die folgende Ausgabe im Terminal angezeigt wird, ist alles korrekt ausgeführt:

Beginnen wir nun mit dem Schreiben von Tests.
Besitzerüberprüfung
Gehen Sie zum Verzeichnis
Finanzierung -> Test und erstellen Sie eine Datei
test_funding.js mit der Erweiterung
.js . Dies ist die Datei, in die die Tests geschrieben werden.
Fügen Sie der Datei
test_funding.js den folgenden Code
hinzu :
const Funding = artifacts.require("Funding"); require("chai").use(require("chai-bignumber")(web3.BigNumber)).should(); contract("Funding", function([account, firstDonator, secondDonator]) { const ETHERS = 10**18; const GAS_PRICE = 10**6; let fundingContract = null; it("should check the owner is valid", async () => { fundingContract = await Funding.deployed(); const owner = await fundingContract.owner.call() owner.should.be.bignumber.equal(account); });
Im Test überprüfen wir, ob im
Finanzierungsvertrag die Adresse des Eigentümers gespeichert ist, der den Vertrag bereitgestellt hat. In unserem Fall ist
account das erste Element des Arrays.
Mit Trüffel können Sie bis zu zehn Adressen verwenden. In Tests benötigen wir nur drei Adressen.
Annahme von Spenden und Überprüfung des Endes der Mittelbeschaffung
In diesem Abschnitt werden wir überprüfen:
- Annahme und Höhe der Spenden.
- Wurde ein bestimmter Spendenbetrag erreicht?
- Was passiert, wenn Sie mehr spenden, als Sie sammeln müssen?
- Der Gesamtbetrag der Spende.
- Kann ich weiterhin Spenden sammeln, wenn der erforderliche Betrag eingezogen wurde?
Wir werden die Tests schreiben und analysieren:
. . . . . . . . . . . . . . . . . . . . . . const ETHERS = 10**18; const GAS_PRICE = 10**6; let fundingContract = null; let txEvent; function findEvent(logs, eventName) { let result = null; for (let log of logs) { if (log.event === eventName) { result = log; break; } } return result; }; it("should accept donations from the donator #1", async () => { const bFirstDonator= web3.eth.getBalance(firstDonator); const donate = await fundingContract.donate({ from: firstDonator, value: 5 * ETHERS, gasPrice: GAS_PRICE }); txEvent = findEvent(donate.logs, "Donated"); txEvent.args.donation.should.be.bignumber.equal(5 * ETHERS); const difference = bFirstDonator.sub(web3.eth.getBalance(firstDonator)).sub(new web3.BigNumber(donate.receipt.gasUsed * GAS_PRICE)); difference.should.be.bignumber.equal(5 * ETHERS); });
Bevor ich den Test analysiere, möchte ich 2 Punkte beachten:
- Um nach Ereignissen (Ereignissen) zu suchen und deren Argumente zu überprüfen, wurde eine kleine Funktion findEvent geschrieben.
- Zur Vereinfachung von Tests und Berechnungen wurde der innere Wert für Gas eingestellt (Konstante GAS_PRICE).
Lassen Sie uns nun den Test analysieren. Im Test haben wir überprüft:
- dass wir Spenden annehmen können, indem wir die donate () -Methode aufrufen;
- dass der uns gespendete Betrag korrekt angegeben ist;
- dass derjenige, der Geld gespendet hat, den Saldo um den gespendeten Betrag verringert hat.
it("should check if donation is not completed", async () => { const isFunded = await fundingContract.isFunded(); isFunded.should.be.equal(false); });
In diesem Test haben wir überprüft, ob die Spendenaktion noch nicht abgeschlossen ist.
it("should not allow to withdraw the fund until the required amount has been collected", async () => { let isCaught = false; try { await fundingContract.withdraw({ gasPrice: GAS_PRICE }); } catch (err) { isCaught = true; } isCaught.should.be.equal(true); });
Im Test haben wir überprüft, dass wir kein Geld abheben können, bis der benötigte Betrag eingezogen ist.
it("should accept donations from the donator #2", async () => { const bSecondDonator= web3.eth.getBalance(secondDonator); const donate = await fundingContract.donate({ from: secondDonator, value: 20 * ETHERS, gasPrice: GAS_PRICE }); txEvent = findEvent(donate.logs, "Donated"); txEvent.args.donation.should.be.bignumber.equal(20 * ETHERS); const difference = bSecondDonator.sub(web3.eth.getBalance(secondDonator)).sub(new web3.BigNumber(donate.receipt.gasUsed * GAS_PRICE)); difference.should.be.bignumber.equal(15 * ETHERS); });
Im Test haben wir überprüft, ob die
donate () -Methode bei einer großen
Spende das Geld berechnet und an die Person zurückgibt, die mehr als nötig gespendet hat. Dieser Betrag ist die Differenz zwischen dem angesammelten Betrag und dem Betrag, den Sie sammeln möchten.
it("should check if the donation is completed", async () => { const notFunded = await fundingContract.isFunded(); notFunded.should.be.equal(true); }); it("should check if donated amount of money is correct", async () => { const raised = await fundingContract.raised.call(); raised.should.be.bignumber.equal(20 * ETHERS); }); it("should not accept donations if the fundraising is completed", async () => { let isCaught = false; try { await fundingContract.donate({ from: firstDonator, value: 10 * ETHERS }); } catch (err) { isCaught = true; } isCaught.should.be.equal(true); });
In diesen drei Tests haben wir Folgendes überprüft:
- dass die Spendenaktion abgeschlossen ist;
- dass der Spendenbetrag korrekt ist;
- dass niemand sonst spenden kann, da die Spendenaktion abgeschlossen ist.
Geld abheben
Im vorherigen Abschnitt des Tutorials haben wir den benötigten Betrag gesammelt. Jetzt kann er angezeigt werden:
. . . . . . . . . . . . . . . . . . . . . . it("should allow the owner to withdraw the fund", async () => { const bAccount = web3.eth.getBalance(account); const withdraw = await fundingContract.withdraw({ gasPrice: GAS_PRICE }); txEvent = findEvent(withdraw.logs, "Withdrew"); txEvent.args.amount.should.be.bignumber.equal(20 * ETHERS); const difference = web3.eth.getBalance(account).sub(bAccount); difference.should.be.bignumber.equal(await fundingContract.raised.call() - withdraw.receipt.gasUsed * GAS_PRICE); });
Durch Aufrufen der Funktion
draw () haben wir das Geld des Inhabers des Smart-Vertrags in unserem Fallkonto
abgehoben . Dann haben wir überprüft, ob wir den benötigten Betrag wirklich abgehoben haben.
Schreiben Sie dazu die Differenz in der Bilanz vor und nach dem Abheben von Geldern in die
Differenzkonstante . Das Ergebnis wird mit der Höhe der Spenden abzüglich der Transaktionsgebühr verglichen. Wie oben erwähnt, habe ich zur Vereinfachung von Tests und Berechnungen meinen eigenen Preis für
Gas festgelegt .
Führen Sie die schriftlichen Tests mit dem Befehl
trüffeltest aus . Wenn alles richtig gemacht wurde, sollte das Ergebnis wie folgt sein:

Ergebnis
Ich habe versucht, die Schritte zum Testen intelligenter Verträge in einer einfachen und verständlichen Sprache zu beschreiben: von der Vorbereitung einer Testumgebung bis zum Schreiben der Tests selbst.
Jetzt können Sie jeden intelligenten Vertrag testen und sicherstellen, dass er ordnungsgemäß funktioniert.