
Les termes du contrat intelligent ne peuvent pas être modifiés. Par conséquent, chaque fois que vous créez un contrat intelligent, vous devez vous assurer qu'il fonctionne correctement. Le test est un moyen sûr de tester un contrat dans différentes situations. Dans ce didacticiel, vous apprendrez les étapes à suivre.
Je dirai:
- Comment préparer un environnement de test.
- Comment écrire des tests JavaScript et les exécuter dans Truffle .
Le didacticiel est conçu pour les débutants qui viennent de commencer à se plonger dans le développement et les tests de contrats intelligents sur Ethereum. Pour comprendre ce tutoriel, vous devez avoir au moins une connaissance de base de
JavaScript et
Solidity .
1. Comment préparer un environnement de test
Il existe de nombreuses façons de tester les contrats intelligents, mais
Truffle est l'outil d'écriture de test le plus populaire utilisant
JavaScript . Sur
Truffle, vous pouvez écrire des tests unitaires, ainsi que réaliser des tests d'intégration à part entière avec des paramètres réels de l'environnement de production.
Vous devez d'abord installer la dernière version de Node.js à partir du
site officiel .
Ouvrez ensuite un terminal et installez
Truffle à l'aide de la commande suivante:
npm install -g truffle
Après avoir installé
Truffle , sans fermer le terminal, créez le répertoire
Funding :
mkdir Funding
Ensuite, allez dans le répertoire avec la commande:
cd Funding
Pour initialiser le répertoire, exécutez la commande:
truffle init
Après avoir exécuté la commande, les dossiers et fichiers suivants seront créés dans le répertoire de
financement :

De plus, nous travaillerons avec chaque répertoire. En attendant, nous continuons de préparer l'environnement de test.
Pour exécuter les tests, vous avez besoin de bibliothèques Javascript.
Mocha est une bibliothèque qui contient des fonctions communes pour les tests, y compris
décrire et
cela .
Chai est une bibliothèque qui prend en charge une variété de fonctions pour les contrôles. Il existe différents «styles» pour vérifier les résultats, et Chai nous offre cette opportunité. Dans le tutoriel, nous utiliserons
Should .
Par défaut, Mocha fait partie de Truffle, nous pouvons facilement utiliser les fonctions de la bibliothèque. Chai doit être installé manuellement. Pour ce faire, utilisez le terminal et dans le répertoire racine du projet, exécutez la commande:
npm install chai
J'ai également installé la
bibliothèque chai-bignumber pour comparer les nombres avec une précision arbitraire:
npm install --save-dev chai-bignumber
L'environnement de test est prêt pour cela. Vous pouvez maintenant commencer à développer un contrat intelligent et à le tester.
2. Comment écrire des tests JavaScript et les exécuter dans Truffle
Pour développer des tests, vous avez besoin d'un contrat intelligent. Nous développerons un contrat qui vous permettra de collecter des dons, de fixer un montant spécifique pour réaliser la collecte et de retirer des fonds. Si quelqu'un donne plus, la différence entre le montant accumulé et le montant à percevoir lui sera restituée.
Accédez au répertoire
Financement -> contrats . Dans
Financement / contrats, créez le fichier
Funding.sol avec l'extension
.sol - ce sera le contrat intelligent pour les tests.
Dans
Funding.sol, ajoutez le code suivant:
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); } }
Le contrat est prêt. Nous déploierons un contrat intelligent via la migration.
Les migrations sont
des fichiers
JavaScript qui vous aident à déployer des contrats sur le réseau Ethereum. C'est le principal moyen de déployer des contrats.
Accédez au répertoire
Funding -> migrations et créez le fichier
2_funding.js en y ajoutant le code suivant:
const Funding = artifacts.require("./Funding.sol"); const ETHERS = 10**18; const GOAL = 20 * ETHERS; module.exports = function(deployer) { deployer.deploy(Funding, GOAL); };
Pour exécuter les tests, vous devez utiliser la commande de
test de truffe . Dans le terminal, accédez à la racine du répertoire
Funding , qui a été créé lors de la préparation de l'environnement de test et entrez:
truffle test
Si la sortie suivante apparaît dans le terminal, alors tout est fait correctement:

Commençons maintenant à écrire des tests.
Vérification du propriétaire
Accédez au répertoire
Funding -> test et créez un fichier
test_funding.js avec l'extension
.js . Il s'agit du fichier dans lequel les tests seront écrits.
Ajoutez le code suivant au fichier
test_funding.js :
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); });
Dans le test, nous vérifions que le contrat de
financement stocke l'adresse du propriétaire qui a déployé le contrat. Dans notre cas, le
compte est le premier élément du tableau.
La truffe vous permet d'utiliser jusqu'à dix adresses, dans les tests, nous n'avons besoin que de trois adresses.
Acceptation des dons et vérification de la fin de la collecte de fonds
Dans cette section, nous allons vérifier:
- Acceptation et montant des dons.
- Un certain montant de don a-t-il été atteint?
- Que se passe-t-il si vous donnez plus que ce dont vous avez besoin?
- Le montant total du don.
- Puis-je continuer à collecter des fonds si le montant requis a été collecté.
Nous rédigerons et analyserons les tests:
. . . . . . . . . . . . . . . . . . . . . . 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); });
Avant d'analyser le test, je veux noter 2 points:
- Pour rechercher des événements (événements) et vérifier ses arguments, une petite fonction findEvent a été écrite.
- Pour faciliter les tests et les calculs, la valeur intrinsèque du gaz a été définie (constante GAS_PRICE).
Analysons maintenant le test. Dans le test, nous avons vérifié:
- que nous pouvons accepter des dons en appelant la méthode donate () ;
- que le montant qui nous est donné est correctement indiqué;
- que celui qui a donné des fonds, le solde a diminué du montant du don.
it("should check if donation is not completed", async () => { const isFunded = await fundingContract.isFunded(); isFunded.should.be.equal(false); });
Dans ce test, nous avons vérifié que la collecte de fonds n'était pas encore terminée.
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); });
Lors du test, nous avons vérifié que nous ne pouvons pas retirer de fonds tant que le montant dont nous avons besoin n'est pas collecté.
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); });
Dans le test, nous avons vérifié que si vous faites un don important, la méthode
donate () calcule et retourne les fonds à la personne qui a fait un don plus que nécessaire. Ce montant est la différence entre le montant accumulé et le montant que vous souhaitez collecter.
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); });
Dans ces trois tests, nous avons vérifié:
- que la collecte de fonds est terminée;
- que le montant du don est correct;
- que personne d'autre ne peut faire un don, car la collecte de fonds est terminée.
Retirer des fonds
Dans la section précédente du tutoriel, nous avons collecté le montant dont nous avons besoin, maintenant il peut être affiché:
. . . . . . . . . . . . . . . . . . . . . . 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); });
En appelant la fonction de
retrait () , nous avons retiré les fonds du propriétaire du contrat intelligent, dans notre
compte de cas. Ensuite, nous avons vérifié que nous avions réellement retiré le montant dont nous avions besoin. Pour ce faire,
inscrivez la différence dans le solde avant et après le retrait de fonds dans la constante de
différence . Le résultat est comparé au montant des dons moins les frais de transaction. Comme mentionné ci-dessus, pour la commodité des tests et des calculs, j'ai fixé mon propre prix pour le
gaz .
Exécutez les tests écrits avec la commande de
test de truffe . Si tout a été fait correctement, le résultat devrait être le suivant:

Résultat
J'ai essayé de décrire les étapes du test de contrats intelligents dans un langage simple et compréhensible: de la préparation d'un environnement de test à la rédaction des tests eux-mêmes.
Vous pouvez maintenant tester n'importe quel contrat intelligent et vous assurer qu'il fonctionne correctement.