Writing Test Cases
In this section, we will go over the different ways we can write test cases for our stateful precompile.
Adding Config Tests
Precompile generation tool generates skeletons for unit tests as well. Generated config tests will be under ./precompile/contracts/helloworld/config_test.go
for Subnet-EVM and ./helloworld/config_test.go
for Precompile-EVM. There are mainly two functions we need to test: Verify
and Equal
. Verify
checks if the precompile is configured correctly. Equal
checks if the precompile is equal to another precompile. Generated Verify
tests contain a valid case.
You can add more invalid cases depending on your implementation. Equal
tests generate some invalid cases to test different timestamps, types, and AllowList cases. You can check each config_test.go
files for other precompiles under the Subnet-EVM's ./precompile/contracts
directory for more examples.
Adding Contract Tests
The tool also generates contract tests to make sure our precompile is working correctly. Generated tests include cases to test allow list capabilities, gas costs, and calling functions in read-only mode. You can check other contract_test.go
files in the /precompile/contracts
. Hello World contract tests will be under ./precompile/contracts/helloworld/contract_test.go
for Subnet-EVM and ./helloworld/contract_test.go
for Precompile-EVM.
We will also add more test to cover functionalities of sayHello()
and setGreeting()
. Contract tests are defined in a standard structure that each test can customize to their needs. The test structure is as follows:
Each test can populate the fields of the PrecompileTest
struct to customize the test. This test uses an AllowList helper function allowlist.RunPrecompileWithAllowListTests(t, Module, state.NewTestStateDB, tests)
which can run all specified tests plus AllowList test suites. If you don't plan to use AllowList, you can directly run them as follows:
Adding VM Tests (Optional)
This is only applicable for direct Subnet-EVM forks as test files are not directly exported in Golang. If you use Precompile-EVM you can skip this step.
VM tests are tests that run the precompile by calling it through the Subnet-EVM. These are the most comprehensive tests that we can run. If your precompile modifies how the Subnet-EVM works, for example changing blockchain rules, you should add a VM test. For example, you can take a look at the TestRewardManagerPrecompileSetRewardAddress
function in here.
For this Hello World example, we don't modify any Subnet-EVM rules, so we don't need to add any VM tests.
Adding Solidity Test Contracts
Let's add our test contract to ./contracts/contracts
. This smart contract lets us interact with our precompile! We cast the HelloWorld
precompile address to the IHelloWorld
interface. In doing so, helloWorld
is now a contract of type IHelloWorld
and when we call any functions on that contract, we will be redirected to the HelloWorld precompile address.
The below code snippet can be copied and pasted into a new file called ExampleHelloWorld.sol
:
Hello World Precompile is a different contract than ExampleHelloWorld and has a different address. Since the precompile uses AllowList for a permissioned access, any call to the precompile including from ExampleHelloWorld will be denied unless the caller is added to the AllowList.
Please note that this contract is simply a wrapper and is calling the precompile functions. The reason why we add another example smart contract is to have a simpler stateless tests.
For the test contract we write our test in ./contracts/test/ExampleHelloWorldTest.sol
.
Last updated on