5/5
_Follow along with this video:_ --- ### Testing and refactoring the performUpkeep Let's give some love to `performUpkeep`, starting with some tests. Starting light, open the `RaffleTest.t.sol` and paste the following: ```solidity function testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public { // Arrange vm.prank(PLAYER); raffle.enterRaffle{value: entranceFee}(); vm.warp(block.timestamp + interval + 1); vm.roll(block.number + 1); // Act / Assert // It doesnt revert raffle.performUpkeep(""); } ``` We prank the `PLAYER` address, then use it to call `enterRaffle` with the correct `entranceFee`. We use the `warp` and `roll` to set `block.timestamp` into the future. Lastly, we call `performUpkeep`. As you've figured out, we are not running any asserts here. But that is ok because if `performUpkeep` had a reason to fail, then it would have reverted and our `forge test` would have caught it. Run the test using: `forge test --mt testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue` It passes, amazing! Keep going! Let's test if `performUpkeep` reverts in case `checkUpkeep` is false: ```solidity function testPerformUpkeepRevertsIfCheckUpkeepIsFalse() public { // Arrange uint256 currentBalance = 0; uint256 numPlayers = 0; Raffle.RaffleState rState = raffle.getRaffleState(); // Act / Assert vm.expectRevert( abi.encodeWithSelector( Raffle.Raffle__UpkeepNotNeeded.selector, currentBalance, numPlayers, rState ) ); raffle.performUpkeep(""); } ``` This can be understood easier if we start from the end. We want to call `performUpkeep` and we expect it to revert. For that, we use the `vm.expectRevert` to indicate that we expect the next call to revert. If we access [this link](https://book.getfoundry.sh/cheatcodes/expect-revert) we can see that in case we use a custom error with parameters we can specify them as follows: ```solidity vm.expectRevert( abi.encodeWithSelector(CustomError.selector, 1, 2) ); ``` In our case the custom error has 3 parameters: ```solidity error Raffle__UpkeepNotNeeded( uint256 currentBalance, uint256 numPlayers, uint256 raffleState ); ``` First parameter: `Raffle.Raffle__UpkeepNotNeeded.selector`; Second parameter: `currentBalance`; Third parameter: `numPlayers`; Fourth parameter: `raffleState`; Out of all of them, the only one available is the first. We define a `currentBalance` and `numPlayers` and assign them both 0. To get the `raffleState` we can use the `getRaffleState` view function. Run the test using: `forge test --mt testPerformUpkeepRevertsIfCheckUpkeepIsFalse` Everything passes, great! I know some concepts haven't been explained. I'm referring to `encodeWithSelector` and the general concept of function selectors. These will be introduced in the next sections. Great work! Now let's further explore events.
Follow along with this video:
Let's give some love to performUpkeep
, starting with some tests.
Starting light, open the RaffleTest.t.sol
and paste the following:
We prank the PLAYER
address, then use it to call enterRaffle
with the correct entranceFee
. We use the warp
and roll
to set block.timestamp
into the future. Lastly, we call performUpkeep
.
As you've figured out, we are not running any asserts here. But that is ok because if performUpkeep
had a reason to fail, then it would have reverted and our forge test
would have caught it.
Run the test using: forge test --mt testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue
It passes, amazing!
Keep going! Let's test if performUpkeep
reverts in case checkUpkeep
is false:
This can be understood easier if we start from the end. We want to call performUpkeep
and we expect it to revert. For that, we use the vm.expectRevert
to indicate that we expect the next call to revert. If we access this link we can see that in case we use a custom error with parameters we can specify them as follows:
In our case the custom error has 3 parameters:
First parameter: Raffle.Raffle__UpkeepNotNeeded.selector
;
Second parameter: currentBalance
;
Third parameter: numPlayers
;
Fourth parameter: raffleState
;
Out of all of them, the only one available is the first. We define a currentBalance
and numPlayers
and assign them both 0. To get the raffleState
we can use the getRaffleState
view function.
Run the test using: forge test --mt testPerformUpkeepRevertsIfCheckUpkeepIsFalse
Everything passes, great!
I know some concepts haven't been explained. I'm referring to encodeWithSelector
and the general concept of function selectors. These will be introduced in the next sections.
Great work! Now let's further explore events.
A precise guide to Testing the `performUpkeep` Function - Learn to verify `performUpkeep` executes only when `checkUpkeep` returns true using Foundry cheatcodes. Master testing specific revert conditions, including custom errors with parameters via `vm.expectRevert` and `abi.encodeWithSelector`.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Foundryup, Foundry Forge, and Anvil
Blockchain Oracles
How to create local Blockchain testnets
How to verify a smart contract
How to write and run smart contract tests
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Guest lecturers:
Last updated on June 10, 2025
Duration: 2h 55min
Duration: 2h 56min
Duration: 26min
Duration: 5h 22min
Course Overview
About the course
Foundryup, Foundry Forge, and Anvil
Blockchain Oracles
How to create local Blockchain testnets
How to verify a smart contract
How to write and run smart contract tests
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Guest lecturers:
Last updated on June 10, 2025