Chai matchers¶
A set of sweet chai matchers, makes your test easy to write and read. Before you can start using the matchers, you have to tell chai to use the solidity plugin:
import chai from "chai";
import { solidity } from "ethereum-waffle";
chai.use(solidity);
Below is the list of available matchers:
Bignumbers¶
Testing equality of big numbers:
expect(await token.balanceOf(wallet.address)).to.equal(993);
Available matchers for BigNumbers are: equal, eq, above, gt, gte, below, lt, lte, least, most.
Emitting events¶
Testing what events were emitted with what arguments:
await expect(token.transfer(walletTo.address, 7))
.to.emit(token, 'Transfer')
.withArgs(wallet.address, walletTo.address, 7);
Note
The matcher will match indexed event parameters of type string or bytes
even if the expected argument is not hashed using keccack256 first.
Testing with indexed bytes or string parameters. These two examples are equivalent
await expect(contract.addAddress("street", "city"))
.to.emit(contract, 'AddAddress')
.withArgs("street", "city");
const hashedStreet = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("street"));
const hashedCity = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("city"));
await expect(contract.addAddress(street, city))
.to.emit(contract, 'AddAddress')
.withArgs(hashedStreet, hashedCity);
Called on contract¶
Testing if function was called on the provided contract:
await token.balanceOf(wallet.address)
expect('balanceOf').to.be.calledOnContract(token);
Called on contract with arguments¶
Testing if function with certain arguments was called on provided contract:
await token.balanceOf(wallet.address)
expect('balanceOf').to.be.calledOnContractWith(token, [wallet.address]);
Revert¶
Testing if transaction was reverted:
await expect(token.transfer(walletTo.address, 1007)).to.be.reverted;
Revert with message¶
Testing if transaction was reverted with certain message:
await expect(token.transfer(walletTo.address, 1007))
.to.be.revertedWith('Insufficient funds');
Change ether balance¶
Testing whether the transaction changes the balance of the account:
await expect(() => wallet.sendTransaction({to: walletTo.address, value: 200}))
.to.changeEtherBalance(walletTo, 200);
await expect(await wallet.sendTransaction({to: walletTo.address, value: 200}))
.to.changeEtherBalance(walletTo, 200);
expect for changeEtherBalance gets one of the following parameters:
transaction call : () => Promise<TransactionResponse>
transaction response : TransactionResponse
Note
changeEtherBalance won’t work if there is more than one transaction mined in the block.
The transaction call should be passed to the expect as a callback (we need to check the balance before the call) or as a transaction response.
The matcher can accept numbers, strings and BigNumbers as a balance change, while the account should be specified either as a Wallet or a Contract.
changeEtherBalance ignores transaction fees by default:
// Default behavior
await expect(await wallet.sendTransaction({to: walletTo.address, value: 200}))
.to.changeEtherBalance(wallet, -200);
// To include the transaction fee use:
await expect(await wallet.sendTransaction({to: walletTo.address, gasPrice: 1, value: 200}))
.to.changeEtherBalance(wallet, -21200, {includeFee: true});
Note
changeEtherBalance calls should not be chained. If you need to check changes of the balance for multiple accounts, you should use the changeEtherBalances matcher.
Change ether balance (multiple accounts)¶
Testing whether the transaction changes balance of multiple accounts:
await expect(() => wallet.sendTransaction({to: walletTo.address, value: 200}))
.to.changeEtherBalances([wallet, walletTo], [-200, 200]);
await expect(await wallet.sendTransaction({to: walletTo.address, value: 200}))
.to.changeEtherBalances([wallet, walletTo], [-200, 200]);
Note
changeEtherBalances calls won’t work if there is more than one transaction mined in the block.
Change token balance¶
Testing whether the transfer changes the balance of the account:
await expect(() => token.transfer(walletTo.address, 200))
.to.changeTokenBalance(token, walletTo, 200);
await expect(() => token.transferFrom(wallet.address, walletTo.address, 200))
.to.changeTokenBalance(token, walletTo, 200);
Note
The transfer call should be passed to the expect as a callback (we need to check the balance before the call).
The matcher can accept numbers, strings and BigNumbers as a balance change, while the account should be specified either as a Wallet or a Contract.
Note
changeTokenBalance calls should not be chained. If you need to check changes of the balance for multiple accounts, you should use the changeTokenBalances matcher.
Change token balance (multiple accounts)¶
Testing whether the transfer changes balance for multiple accounts:
await expect(() => token.transfer(walletTo.address, 200))
.to.changeTokenBalances(token, [wallet, walletTo], [-200, 200]);
Proper address¶
Testing if a string is a proper address:
expect('0x28FAA621c3348823D6c6548981a19716bcDc740e').to.be.properAddress;
Proper private key¶
Testing if a string is a proper private key:
expect('0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c5').to.be.properPrivateKey;
Proper hex¶
Testing if a string is a proper hex value of given length:
expect('0x70').to.be.properHex(2);
Hex Equal¶
Testing if a string is a proper hex with value equal to the given hex value. Case insensitive and strips leading zeros:
expect('0x00012AB').to.hexEqual('0x12ab');
Deprecated matchers¶
Change balance¶
Deprecated since version 3.1.2: Use changeEtherBalance() instead.
Testing whether the transaction changes the balance of the account:
await expect(() => wallet.sendTransaction({to: walletTo.address, gasPrice: 0, value: 200}))
.to.changeBalance(walletTo, 200);
await expect(await wallet.sendTransaction({to: walletTo.address, gasPrice: 0, value: 200}))
.to.changeBalance(walletTo, 200);
expect for changeBalance gets one of the following parameters:
transaction call : () => Promise<TransactionResponse>
transaction response : TransactionResponse
Note
changeBalance won’t work if there is more than one transaction mined in the block.
The transaction call should be passed to the expect as a callback (we need to check the balance before the call) or as a transaction response.
The matcher can accept numbers, strings and BigNumbers as a balance change, while the account should be specified either as a Wallet or a Contract.
Note
changeBalance calls should not be chained. If you need to check changes of the balance for multiple accounts, you should use the changeBalances matcher.
Change balance (multiple accounts)¶
Deprecated since version 3.1.2: Use changeEtherBalances() instead.
Testing whether the transaction changes balance of multiple accounts:
await expect(() => wallet.sendTransaction({to: walletTo.address, gasPrice: 0, value: 200}))
.to.changeBalances([wallet, walletTo], [-200, 200]);
await expect(await wallet.sendTransaction({to: walletTo.address, gasPrice: 0, value: 200}))
.to.changeBalances([wallet, walletTo], [-200, 200]);
Note
changeBalances calls won’t work if there is more than one transaction mined in the block.