Comment tester les contrats intelligents

image

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:

  1. Comment préparer un environnement de test.
  2. 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 :

image

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:

image

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:

  1. Acceptation et montant des dons.
  2. Un certain montant de don a-t-il été atteint?
  3. Que se passe-t-il si vous donnez plus que ce dont vous avez besoin?
  4. Le montant total du don.
  5. 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:

  1. Pour rechercher des événements (événements) et vérifier ses arguments, une petite fonction findEvent a été écrite.
  2. 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:

image

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.

Source: https://habr.com/ru/post/fr432012/


All Articles