A simple, fast, and easy-to-use Solidity test generator based on the Branching Tree Technique.
[!WARNING]
bulloak
is still0.*.*
, so breaking changes may occur at any time. If you must depend onbulloak
, we recommend pinning to a specific version, i.e.,=0.y.z
.
bash
cargo install bulloak
bulloak
implements two commands:
bulloak scaffold
bulloak check
Say you have a foo.tree
file with the following contents:
tree
FooTest
└── When stuff called
└── It should revert.
You can use bulloak scaffold
to generate a solidity contract
containing modifiers and tests that match the spec described in
foo.tree
. This will be printed to stdout
.
```terminal $ bulloak scaffold foo.tree pragma solidity 0.8.0;
contract FooTest { modifier whenStuffCalled() { _; }
function testRevertWhenStuffCalled() external whenStuffCalled { // It should revert. } } ```
You can use the -w
option to write the generated contracts
to the file system. Say we have a bunch of .tree
files in
the current working directory. If we run the following:
text
$ bulloak scaffold -w ./**/*.tree
bulloak
will create a .t.sol
file per .tree
file and write
the generated contents to it.
If a .t.sol
file's title matches a .tree
in the same directory,
then bulloak
will skip writing to that file. However, you may
override this behaviour with the -f
flag. This will force bulloak
to overwrite the contents of the file.
text
$ bulloak scaffold -wf ./**/*.tree
You can use bulloak check
to make sure that your solidity files
match your spec. For example, any missing tests will be reported
to you.
Say you have the following spec:
tree
HashPairTest
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `keccak256(abi.encodePacked(a,b))`.
└── When first arg is bigger than second arg
└── It should match the result of `keccak256(abi.encodePacked(b,a))`.
And a matching solidity file:
```solidity pragma solidity 0.8.0;
contract HashPairTest { function test_ShouldNeverRevert() external { // It should never revert. }
modifier whenFirstArgIsSmallerThanSecondArg() { _; }
function test_WhenFirstArgIsSmallerThanSecondArg()
external
whenFirstArgIsSmallerThanSecondArg
{
// It should match the result of keccak256(abi.encodePacked(a,b))
.
}
}
```
This solidity file is missing the tests for When first arg is bigger than second arg
,
which would be reported after running bulloak check
, like so:
text
Codegen not found: Couldn't find a corresponding element for "whenFirstArgIsBiggerThanSecondArg" in the solidity file.
Codegen not found: Couldn't find a corresponding element for "test_WhenFirstArgIsBiggerThanSecondArg" in the solidity file.
The following rules are currently implemented:
.tree
& .t.sol
.bulloak scaffold
, is present in the solidity file.bulloak scaffold
, matches the spec order.
bulloak scaffold
are checked. This means that any number of extra functions, modifiers, etc. can be added to the file.Another feature of bulloak
is reporting errors in your input trees.
For example, say you have a buggy foo.tree
file, which is missing a
└
character. Running bulloak scaffold foo.tree
would report the error like this:
``text
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
bulloak error: unexpected
when` keyword
── when the id references a null stream ^^^^
--- (line 2, column 4) --- file: foo.tree ```
bulloak scaffold
scaffolds solidity test files based on .tree
specifications
that follow the Branching Tree Technique.
Currently, there is on-going discussion on how to handle different edge-cases to better empower the solidity community. This section is a description of the current implementation of the compiler.
when/given
branches of a tree.it
branches of a tree. Every action is a leaf node of the tree.Each tree
file should describe a function under test. Trees follow these rules:
bulloak
expects you to use ├
and └
characters to denote branches.when
, given
or it
.when
or given
, it is a condition.when
and given
are interchangeable.it
, it is an action.it
is the same as It
and IT
.Take the following solidity function:
solidity
function hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? hash(a, b) : hash(b, a);
}
A reasonable spec for the above function would be:
tree
HashPairTest
├── It should never revert.
├── When first arg is smaller than second arg
│ └── It should match the result of `keccak256(abi.encodePacked(a,b))`.
└── When first arg is bigger than second arg
└── It should match the result of `keccak256(abi.encodePacked(b,a))`.
There is a top-level action which would generate a test to check the function invariant that it should never revert.
Then, we have the two possible preconditions: a < b
and a >= b
. Both branches end in an action that will make bulloak scaffold
generate the respective test.
Note the following things:
bulloak
also supports actions with sibling conditions, but this might get removed in a future version per this discussion.There are a few things to keep in mind about the scaffolded solidity test:
.tree
but with a .t.sol
extension. E.g. test.tree
would correspond to test.t.sol
..tree
file.Please refer to CONTRIBUTING.md.
This project is licensed under either of: