Passer au contenu principal

Test d'un contrat intelligent simple avec la bibliothèque Waffle

contrats intelligentssolidityWaffletest
Débutant
Ewa Kowalska
26 février 2021
6 minutes de lecture minute read

Dans ce tutoriel, vous apprendrez à

  • Tester les modifications du solde du portefeuille
  • Tester l'émission d'événements avec les arguments spécifiés
  • Confirmer qu'une transaction a été annulée

Hypothèses

  • Vous pouvez créer un nouveau projet JavaScript ou TypeScript
  • Vous avez une expérience de base en matière de tests JavaScript
  • Vous avez utilisé des gestionnaires de paquets comme yarn ou npm
  • Vous possédez des connaissances de base en matière de contrats intelligents et de Solidity

Premiers pas

Le tutoriel décrit l'installation et l'exécution du test en utilisant yarn, mais il n'y a pas de problème si vous préférez npm - je fournirai les références appropriées à la documentation officielle de Waffle.(opens in a new tab)

Installer les dépendances

Ajoutez(opens in a new tab) dépendances ethereum-waffle et typescript aux dépendances de développement de votre projet.

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

Exemple de contrat intelligent

Au cours du tutoriel, nous allons travailler sur un exemple de contrat intelligent simple - EtherSplitter. Il ne fait pas grand-chose à part permettre à quelqu'un d'envoyer des wei et de les répartir équitablement entre deux destinataires prédéfinis. La fonction de séparation nécessite que le nombre de wei soit pair, sinon elle s'inverse. Pour les deux destinataires, il effectue un transfert de wei suivi de l'émission de l'événement Transfert.

Placez le fragment de code EtherSplitter dans 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, 'Uneven wei amount not allowed');
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}
Afficher tout
Copier

Compiler le contrat

Pour compiler(opens in a new tab) le contrat, ajoutez l'entrée suivante au fichier package.json :

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

Ensuite, créez un fichier de configuration avec Waffle, dans le répertoire principal des projets, - waffle.json - puis collez-y la configuration suivante :

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

Exécutez yarn build. Cela fera apparaître le dossier build avec le contrat compilé EtherSplitter au format JSON.

Configuration du test

Tester avec Waffle nécessite d'utiliser des correspondances Chai et Mocha, vous devez donc les ajouter(opens in a new tab) à votre projet. Lancez la mise à jour de votre paquet package.json, et ajoutez le texted'entrée, dans la partie modèle:

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

Si vous voulez faire(opens in a new tab) vos propres tests, exécutez juste le yarn test .

Tests

Maintenant, créez le dossier test et créez le nouveau fichier test\EtherSplitter.test.ts. Copiez le fragment ci-dessous et collez-le dans notre fichier de test.

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("Ether Splitter", () => {
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 // add the tests here
20})
Afficher tout

Quelques mots avant de commencer. Le fournisseur de services, MockProvider, propose une version fictive de la blockchain. Il fournit également des portefeuilles fictifs, qui serviront de base pour tester le contrat intelligent EtherSplitter. Nous pouvons obtenir jusqu'à dix portefeuilles en appelant la méthode getWallets() sur le fournisseur. Dans l'exemple, nous avons trois portefeuilles - pour l'expéditeur et pour deux destinataires.

Ensuite, nous déclarons une variable appelée « splitter » - c'est notre contrat fictif EtherSpliter. Il est créé avant chaque exécution d'un test unique par la méthode deployContract. Cette méthode simule le déploiement d'un contrat à partir du portefeuille transmis en tant que premier paramètre (le portefeuille de l'expéditeur dans notre cas). Le deuxième paramètre est l'ABI et le bytecode du contrat testé - nous transmettons le fichier json du contrat EtherSplitter compilé à partir du répertoire build. Le troisième paramètre est un tableau contenant les arguments du constructeur du contrat qui, dans notre cas, sont les deux adresses des destinataires.

changeBalances

Tout d'abord, nous vérifierons si la méthode fractionnée modifie réellement les soldes des portefeuilles des destinataires. Si nous divisons 50 wei du compte des expéditeurs, nous nous attendons à ce que les soldes des deux destinataires augmentent de 25 wei. Nous utiliserons la correspondance changeBalances de Waffle:

1it("Changes accounts balances", async () => {
2 await expect(() => splitter.split({ value: 50 })).to.changeBalances(
3 [receiver1, receiver2],
4 [25, 25]
5 )
6})

Comme premier paramètre de la correspondance, nous transmettons un tableau de portefeuilles de destinataires, et comme deuxième, un tableau d'augmentations attendues sur les comptes correspondants. Si nous voulions vérifier le solde d'un portefeuille spécifique, nous pourrions également utiliser la correspondance changeBalance , qui ne nécessite pas de transmettre des tableaux, comme dans l'exemple ci-dessous:

1it("Changes account balance", async () => {
2 await expect(() => splitter.split({ value: 50 })).to.changeBalance(
3 receiver1,
4 25
5 )
6})

Notez que dans les deux cas de changeBalance et changeBalances, nous transmettons la fonction de séparation comme callback car le correspondant a besoin d'accéder à l'état des balances avant et après l'appel.

Nous allons ensuite déterminer si l'événement Transfert a été émis après chaque transfert de wei. Nous allons passer à une autre correspondance de Waffle :

Emit

1it("Emits event on the transfer to the first receiver", async () => {
2 await expect(splitter.split({ value: 50 }))
3 .to.emit(splitter, "Transfer")
4 .withArgs(sender.address, receiver1.address, 25)
5})
6
7it("Emits event on the transfer to the second receiver", async () => {
8 await expect(splitter.split({ value: 50 }))
9 .to.emit(splitter, "Transfer")
10 .withArgs(sender.address, receiver2.address, 25)
11})
Afficher tout

La correspondance emit nous permet de vérifier si un contrat a émis un événement en appelant une méthode. En tant que paramètres pour la corrrespondance emit, nous fournissons le contrat fictif que nous prévoyons pour émettre l'événement, ainsi que le nom de cet événement. Dans notre cas, le contrat fictif est splitter et le nom de l'événement Transfer. Nous pouvons également vérifier les valeurs précises des arguments avec lesquels l'événement a été émis - nous transmettons autant d'arguments au withArgs correspondant, comme le prévoit notre déclaration d'événement. Dans le cas du contrat EtherSpliter, nous passons les adresses de l'expéditeur et du destinataire avec le montant en wei transféré.

revertedWith

Comme dernier exemple, nous allons vérifier si la transaction a été annulée en cas de nombre impair de wei. Nous allons utiliser la correspondance revertedWith :

1it("Reverts when Vei amount uneven", async () => {
2 await expect(splitter.split({ value: 51 })).to.be.revertedWith(
3 "Uneven wei amount not allowed"
4 )
5})

Le test, s'il est accepté, nous assurera que la transaction a bien été annulée. Cependant, il doit également y avoir une correspondance exacte entre les messages que nous avons passés dans la déclaration require et le message attendu dans revertedWith. Si nous revenons au code du contrat EtherSpliter, dans la déclaration require pour le montant wei, nous fournissons le message: "Uneven wei amount not allowed". Cela correspond au message que nous attendons dans notre test. Si elles n'étaient pas égales, le test échouerait.

Félicitations !

Vous avez fait votre premier (grand) pas vers les tests des contrats intelligents avec Waffle ! Vous pourriez être intéressé par d'autres tutoriels Waffle :

  • Tester ERC20 avec Waffle
  • Waffle: Bouchonnage dynamique et tests de contrats
  • Tutoriel pour "dire bonjour au monde" avec hardhat et ethers

Dernière modification: @nhsz(opens in a new tab), 27 février 2024

Ce tutoriel vous a été utile ?