-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Component
Forge
Describe the feature you would like
This came up in the support chat and I think it's a reasonable feature to support:
Is there a forge command to create test files? If not does forge add test seem like a valuable addition?
Creating test file typically involves repeated steps:
- Creating the test file
- Adding the necessary imports
- Adding the setup code for the test contract
^ This is the most minimal case, but depending on how tests should be structure, this can take different forms. Here are some ideas on what this could look like:
Creating a simple test file
For example, if I have a contract Counter
that lives in src/Counter.sol
and say I run
$ forge create-test Counter
Then this would create test/Counter.t.sol
with the following content:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
}
}
So in a "convention over configuration" fashion, this would derive the test contracts name and file name based on the to-be-tested contract.
Appendix: Scaffold test functions for methods as well
We could probably even be smart, look into the existing contract, figure out all functions that need test and add dedicated (failing) test*
functions as well.
So given a source that looks like:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}
We can figure out that setNumber()
and increment()
need to be tested, so when generating a test file, the contents would look like this:
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
}
function test_setNumber() public {
assert(false, "NOT IMPLEMENTED")
}
function test_increment() public {
assert(false, "NOT IMPLEMENTED")
}
}
If we go down that route, we might even take it yet a step further.
Support different scaffold types
There a various styles and best practices when writing tests in foundry. While the draft above sets up simple test files that get you going, they probably don't scale very well when the contracts to be tested are big.
One of the practices one can see in bigger projects is that every contract function to be tested gets its own test contract so it can be used as "describe()" block.
For example, the Counter
example above, could then be written as (ignore the fact that this is overkill for this kind of contract):
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Counter.sol";
contract Counter_Test is Test {
Counter public counter;
function setUp() public virtual {
counter = new Counter();
}
}
contract SetNumber_Test is Counter_Test {
function setUp() public override {
Counter_Test.setup();
}
function test_SetNumber() public {
// write assertions
}
}
contract Increment_Test is Counter_Test {
function setUp() public override {
Counter_Test.setup();
}
function test_Increment() public {
// write assertions
}
}
Something like that could then be generated using something like
$ forge create-test Counter --scaffold-type describe # or something else
Consider introducing test type commands
As a matter of fact, given that there's different types of tests as well we might even want to introduce test type commands altogether:
$ forge create-unit-test Counter
or
$ forge create-fuzz-test Counter
Make this part of an over all contract scaffold command
At this point, we might as well think about introducing a dedicated commands to scaffold entire contracts, including their test counterparts. So for example:
$ forge create-contract Counter
Would create src/Counter.sol
with the contents:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
constructor() {
}
}
It would also create src/test/Counter.t.sol
and script/Counter.s.sol
respectively.
Foundry could then have some default test style config options in foundry.toml
that are used when generating those source files.
Additional context
Most of the above is something one can typically find in CLI tools for other platforms and frameworks as well.
For example angular-cli
and vue-cli
provide commands to easily spin up components including their tests.
I think we can draw some inspiration from there.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status