pragmasolidity 0.8.18;contract Event {eventTransfer(addressindexed from, addressindexed to, uint256 amount);functiontransfer(address from,address to,uint256 amount) external {emitTransfer(from, to, amount); }functiontransferMany(address from,address[] calldata to,uint256[] calldata amounts) external {for (uint256 i =0; i < to.length; i++) {emitTransfer(from, to[i], amounts[i]); } }}
Test file:
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.18;import"forge-std/Test.sol";import {Event} from"../src/Event.sol";// forge test --match-path test/Event.t.sol -vvvvcontractEventTestisTest { Event public e;eventTransfer(addressindexed from, addressindexed to, uint256 amount);functionsetUp() public { e =newEvent(); }functiontestEmitTransferEvent() public {// function expectEmit(// bool checkTopic1,// bool checkTopic2,// bool checkTopic3,// bool checkData// ) external;// 1. Tell Foundry which data to check// Check index 1, index 2 and data vm.expectEmit(true,true,false,true);// 2. Emit the expected eventemitTransfer(address(this),address(123),456);// 3. Call the function that should emit the event e.transfer(address(this),address(123),456);// Check only index 1 vm.expectEmit(true,false,false,false);emitTransfer(address(this),address(123),456);// NOTE: index 2 and data (amount) doesn't match// but the test will still pass e.transfer(address(this),address(111),222); }functiontestEmitManyTransferEvent() public {address[] memory to =newaddress[](2); to[0] =address(111); to[1] =address(222);uint256[] memory amounts =newuint[](2); amounts[0] =1; amounts[1] =2;for (uint256 i =0; i < to.length; i++) {// 1. Tell Foundry which data to check vm.expectEmit(true,true,false,true);// 2. Emit the expected eventemitTransfer(address(this), to[i], amounts[i]); }// 3. Call the function that should emit the event e.transferMany(address(this), to, amounts); }}
Test event example 1
To test event, follow these 3 steps:
Tell Foundry which data to check
Emit the expected event
Call the function that should emit the event
In step 1, we call vm.expectEmit(). Here is its definition:
The checked event must be indexed. For example, our Transfer event has two indexed parameters:
eventTransfer(addressindexed from, addressindexed to, uint256 amount);
Writing a test case:
functiontestEmitTransferEvent() public {// function expectEmit(// bool checkTopic1,// bool checkTopic2,// bool checkTopic3,// bool checkData// ) external;// 1. Tell Foundry which data to check// Check index 1, index 2 and data vm.expectEmit(true,true,false,true);// 2. Emit the expected eventemitTransfer(address(this),address(123),456);// 3. Call the function that should emit the event e.transfer(address(this),address(123),456);}
Test event example 2
We can choose to only test some of the parameters:
functiontestEmitTransferEvent() public {// Check only index 1 vm.expectEmit(true,false,false,false);emitTransfer(address(this),address(123),456);// NOTE: index 2 and data (amount) doesn't match// but the test will still pass e.transfer(address(this),address(111),222);}
In this case, if index 1 matches then the test will pass.
Test multiple events
To test multiple events emitted by a single function, we put step 1 and step 2 into a for loop, and then call the function outside the for loop:
functiontestEmitManyTransferEvent() public {address[] memory to =newaddress[](2); to[0] =address(111); to[1] =address(222);uint256[] memory amounts =newuint[](2); amounts[0] =1; amounts[1] =2;for (uint256 i =0; i < to.length; i++) {// 1. Tell Foundry which data to check vm.expectEmit(true,true,false,true);// 2. Emit the expected eventemitTransfer(address(this), to[i], amounts[i]); }// 3. Call the function that should emit the event e.transferMany(address(this), to, amounts);}