Waffle: Dinamik taklit ve sözleşme çağrılarını test etme
Bu öğretici ne ile ilgili?
Bu eğitimde şunları nasıl yapacağınızı öğreneceksiniz:
- dinamik taklit kullanımı
- akıllı sözleşmeler arasındaki test etkileşimleri
Varsayımlar:
Solidity
'de basit bir akıllı sözleşmenin nasıl yazılacağını zaten biliyorsunuzJavaScript
veTypeScript
'e aşinasınız- başka
Waffle
öğreticilerini tamamladınız veya bu konuda bir iki şey biliyorsunuz
Dinamik taklit
Dinamik taklit neden yararlıdır? Şey, entegrasyon testleri yerine birim testleri yazmamıza izin veriyor. Bu ne demek? Bu, akıllı sözleşmelerin bağımlılıkları hakkında endişelenmemize gerek olmadığı anlamına gelir, böylece hepsini tamamen ayrı ayrı test edebiliriz. Size tam olarak nasıl yapabileceğinizi göstermeme izin verin.
1. Proje
Başlamadan önce basit bir node.js projesi hazırlamamız gerekiyor:
mkdir dynamic-mockingcd dynamic-mockingmkdir contracts srcyarn init# or if you're using npmnpm init
Typescript ve test bağımlılıkları ekleyerek başlayalım - mocha ve chai:
yarn add --dev @types/chai @types/mocha chai mocha ts-node typescript# or if you're using npmnpm install @types/chai @types/mocha chai mocha ts-node typescript --save-dev
Şimdi Waffle
ve ethers
ekleyelim:
yarn add --dev ethereum-waffle ethers# or if you're using npmnpm install ethereum-waffle ethers --save-dev
Proje yapınız şimdi şöyle görünmeli:
1.2├── contracts3├── package.json4└── test
2. Akıllı sözleşme
Dinamik taklit etmeye başlamak için bağımlılıkları olan akıllı bir sözleşmeye ihtiyacımız var. Kaygılanmayın, bunu size anlatacağım!
İşte tek amacı zengin olup olmadığımızı kontrol etmek olan Solidity
ile yazılmış basit bir akıllı sözleşme. Yeterli token'ımız olup olmadığını kontrol etmek için ERC20 token'ını kullanır. Onu ./contracts/AmIRichAlready.sol
içine koyun.
1pragma solidity ^0.6.2;23interface IERC20 {4 function balanceOf(address account) external view returns (uint256);5}67contract AmIRichAlready {8 IERC20 private tokenContract;9 uint public richness = 1000000 * 10 ** 18;1011 constructor (IERC20 _tokenContract) public {12 tokenContract = _tokenContract;13 }1415 function check() public view returns (bool) {16 uint balance = tokenContract.balanceOf(msg.sender);17 return balance > richness;18 }19}Tümünü gösterKopyala
Dinamik taklit kullanmak istediğimiz için tüm ERC20'ye ihtiyacımız yok, bu yüzden IERC20 arayüzünü sadece bir fonksiyonda kullanıyoruz.
Bu sözleşmeyi yapma zamanı! Bunun için Waffle
kullanacağız. İlk olarak, derleme seçeneklerini belirten basit bir waffle.json
yapılandırma dosyası oluşturacağız.
1{2 "compilerType": "solcjs",3 "compilerVersion": "0.6.2",4 "sourceDirectory": "./contracts",5 "outputDirectory": "./build"6}Kopyala
Artık Waffle ile sözleşme yapmaya hazırız:
npx waffle
Kolay, değil mi? build/
klasöründe sözleşmeye ve arayüze karşılık gelen iki dosya belirdi. Onları daha sonra test için kullanacağız.
3. Test
Gerçek test için AmIRichAlready.test.ts
adında bir dosya oluşturalım. Her şeyden önce, ithalatı halletmemiz gerekiyor. Onlara daha sonra ihtiyaç duyacağız:
1import { expect, use } from "chai"2import { Contract, utils, Wallet } from "ethers"3import {4 deployContract,5 deployMockContract,6 MockProvider,7 solidity,8} from "ethereum-waffle"
JS bağımlılıkları dışında, yerleşik sözleşmemizi ve arayüzümüzü içe aktarmamız gerekiyor:
1import IERC20 from "../build/IERC20.json"2import AmIRichAlready from "../build/AmIRichAlready.json"
Waffle test için chai
kullanır. Ancak kullanmadan önce, Waffle'ın eşleyicilerini chai'nin kendisine enjekte etmemiz gerekiyor:
1use(solidity)
Her testten önce sözleşmenin durumunu sıfırlayacak beforeEach()
fonksiyonunu uygulamamız gerekiyor. Önce orada neye ihtiyacımız olduğunu düşünelim. Bir sözleşmeyi dağıtmak için iki şeye ihtiyacımız var: Bir cüzdan ve onu AmIRichAlready
sözleşmesi için bir argüman olarak iletmek üzere konuşlandırılmış bir ERC20 sözleşmesi.
İlk olarak bir cüzdan oluşturuyoruz:
1const [wallet] = new MockProvider().getWallets()
O zaman bir ERC20 sözleşmesi dağıtmamız gerekiyor. İşin zor yanı şu: Elimizde sadece bir arayüz var. Waffle'ın bizi kurtarmaya geldiği kısım burası. Waffle'ın sihirli deployMockContract()
fonksiyonu sadece arayüzün abi'sini kullanarak bir sözleşme oluşturur:
1const mockERC20 = await deployMockContract(wallet, IERC20.abi)
Şimdi hem cüzdan hem de dağıtılan ERC20 ile devam edip AmIRichAlready
sözleşmesini uygulayabiliriz:
1const contract = await deployContract(wallet, AmIRichAlready, [2 mockERC20.address,3])
Bunların tamamı ile, beforeEach()
fonksiyonumuz tamamlandı. Şimdiye dek AmIRichAlready.test.ts
dosyanız şu şekilde gözükmeli:
1import { expect, use } from "chai"2import { Contract, utils, Wallet } from "ethers"3import {4 deployContract,5 deployMockContract,6 MockProvider,7 solidity,8} from "ethereum-waffle"910import IERC20 from "../build/IERC20.json"11import AmIRichAlready from "../build/AmIRichAlready.json"1213use(solidity)1415describe("Am I Rich Already", () => {16 let mockERC20: Contract17 let contract: Contract18 let wallet: Wallet1920 beforeEach(async () => {21 ;[wallet] = new MockProvider().getWallets()22 mockERC20 = await deployMockContract(wallet, IERC20.abi)23 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address])24 })25})Tümünü göster
Hadi AmIRichAlready
sözleşmesine ilk testi yazalım. Sizce testimiz ne hakkında olmalı? Evet, haklısınız! Zaten zengin olup olmadığımızı kontrol etmeliyiz :)
Ama bir saniye durun. Taklit sözleşmemiz hangi değerlerin döndürüleceğini nasıl bilecek? balanceOf()
fonksiyonu için herhangi bir mantık eklemedik. Tekrardan, Waffle burada yardımcı olabilir. Sahte sözleşmemizde şimdi bazı yeni ilginç şeyler var:
1await mockERC20.mock.<nameOfMethod>.returns(<value>)2await mockERC20.mock.<nameOfMethod>.withArgs(<arguments>).returns(<value>)
Bu bilgiyle nihayet ilk testimizi yazabiliriz:
1it("returns false if the wallet has less than 1000000 tokens", async () => {2 await mockERC20.mock.balanceOf.returns(utils.parseEther("999999"))3 expect(await contract.check()).to.be.equal(false)4})
Bu testi parçalara ayıralım:
- Taklit ERC20 sözleşmemizi her zaman 999999 token'lık bakiyeyi iade edecek şekilde ayarladık.
contract.check()
yöntemininfalse
döndürüp döndürmediğini kontrol edin.
Canavarı başlatmaya hazırız:
Yani, test işe yarıyor ama... biraz daha geliştirilebilir. balanceOf()
fonksiyonu her zaman 99999 döndürür. Fonksiyonun bir şey döndürmesi gereken bir cüzdan belirterek onu iyileştirebiliriz: Tıpkı gerçek bir sözleşme gibi:
1it("returns false if the wallet has less than 1000001 tokens", async () => {2 await mockERC20.mock.balanceOf3 .withArgs(wallet.address)4 .returns(utils.parseEther("999999"))5 expect(await contract.check()).to.be.equal(false)6})
Şimdiye kadar sadece yeterince zengin olmadığımız durumu test ettik. Bunun yerine zıttını test edelim:
1it("returns true if the wallet has at least 1000001 tokens", async () => {2 await mockERC20.mock.balanceOf3 .withArgs(wallet.address)4 .returns(utils.parseEther("1000001"))5 expect(await contract.check()).to.be.equal(true)6})
Testleri çalıştırırsınız...
...ve buradasınız! Sözleşmemiz istendiği gibi çalışıyor gibi görünüyor :)
Sözleşme çağrılarını test etme
Şimdiye kadar yaptıklarımı özetleyelim. AmIRichAlready
sözleşmemizin işlevselliğini test ettik ve düzgün çalışıyor gibi görünüyor. Bu işimizin bittiği anlamına gelir, değil mi? Tam olarak değil! Waffle, sözleşmemizi daha da test etmemizi sağlıyor. Ama nasıl? Waffle'ın zulasıda calledOnContract()
ve calledOnContractWith()
eşleyicileri bulunmaktadır. Sözleşmemizin ERC20 taklit sözleşme olarak adlandırılıp adlandırılmadığını kontrol etmemizi sağlayacaklar. İşte bu eşleyicilerden biriyle yapılan temel bir test:
1it("checks if contract called balanceOf on the ERC20 token", async () => {2 await mockERC20.mock.balanceOf.returns(utils.parseEther("999999"))3 await contract.check()4 expect("balanceOf").to.be.calledOnContract(mockERC20)5})
Daha da ileri gidebilir ve size bahsettiğim diğer eşleyiciyle bu testi iyileştirebiliriz:
1it("checks if contract called balanceOf with certain wallet on the ERC20 token", async () => {2 await mockERC20.mock.balanceOf3 .withArgs(wallet.address)4 .returns(utils.parseEther("999999"))5 await contract.check()6 expect("balanceOf").to.be.calledOnContractWith(mockERC20, [wallet.address])7})
Testlerin doğru olup olmadığını kontrol edelim:
Müthiş, tüm testler yeşil ışık yakıyor.
Waffle ile sözleşme çağrılarını test etmek aşırı kolaydır. En güzel tarafı ise şu: Bu eşleyiciler hem normal hem de taklit sözleşmelerle çalışır! Bunun nedeni, Waffle'ın diğer teknolojiler için popüler test kütüphanelerinde olduğu gibi, kod enjekte etmek yerine EVM çağrılarını kaydetmesi ve filtrelemesidir.
Bitiş Çizgisi
Tebrikler! Artık sözleşme çağrılarını test etmek ve sözleşmeleri dinamik olarak taklit etmek için Waffle'ı nasıl kullanacağınızı biliyorsunuz. Keşfedilecek çok daha ilginç özellikler var. Waffle'ın belgelerine dalmanızı öneririm.
Waffle'ın belgeleri burada(opens in a new tab) mevcuttur.
Bu öğreticinin kaynak kodu burada(opens in a new tab) bulunabilir.
Ayrıca ilginizi çekebilecek öğreticiler:
Son düzenleme: @nhsz(opens in a new tab), 27 Şubat 2024