pragma solidity 0.8.18;
contract Event {
event Transfer(address indexed from, address indexed to, uint256 amount);
function transfer(address from, address to, uint256 amount) external {
emit Transfer(from, to, amount);
}
function transferMany(address from, address[] calldata to, uint256[] calldata amounts) external {
for (uint256 i = 0; i < to.length; i++) {
emit Transfer(from, to[i], amounts[i]);
}
}
}
Test file:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;
import "forge-std/Test.sol";
import {Event} from "../src/Event.sol";
// forge test --match-path test/Event.t.sol -vvvv
contract EventTest is Test {
Event public e;
event Transfer(address indexed from, address indexed to, uint256 amount);
function setUp() public {
e = new Event();
}
function testEmitTransferEvent() 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 event
emit Transfer(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);
emit Transfer(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);
}
function testEmitManyTransferEvent() public {
address[] memory to = new address[](2);
to[0] = address(111);
to[1] = address(222);
uint256[] memory amounts = new uint[](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 event
emit Transfer(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:
event Transfer(address indexed from, address indexed to, uint256 amount);
Writing a test case:
function testEmitTransferEvent() 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 event
emit Transfer(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:
function testEmitTransferEvent() public {
// Check only index 1
vm.expectEmit(true, false, false, false);
emit Transfer(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:
function testEmitManyTransferEvent() public {
address[] memory to = new address[](2);
to[0] = address(111);
to[1] = address(222);
uint256[] memory amounts = new uint[](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 event
emit Transfer(address(this), to[i], amounts[i]);
}
// 3. Call the function that should emit the event
e.transferMany(address(this), to, amounts);
}