diff --git a/website/docs/contribution/testing.mdx b/website/docs/contribution/testing.mdx index fd6784d3..485c89be 100644 --- a/website/docs/contribution/testing.mdx +++ b/website/docs/contribution/testing.mdx @@ -1,114 +1,120 @@ --- sidebar_position: 4 title: Testing -description: Guidelines for testing Compose facets and libraries +description: Compose tests are **behavior-first** and primarily written with Foundry fuzz tests. --- -## Recommended -- Write tests for new functionality -- Test your changes thoroughly -- Ensure test coverage is maintained or improved +Compose tests are **behavior-first** and primarily written with Foundry fuzz tests. -If you make changes to existing functionality, please make sure that the existing tests still work for that functionality and write new tests as necessary that cover the changes you made. +## Testing Philosophy -Please note that you can submit a pull request for new functionality **without tests**. Another person can write tests for new functionality. +- **BTT (Behaviour Tree Testing):** specs in `test/trees/*.tree` are the source of truth. +- **Facet/mod parity:** test both facet surfaces and modifier/module behaviour where applicable. +- **No test-only code in production:** keep test helpers under `test/` (`harnesses`, `storage`, base fixtures). +- **Behaviour-oriented naming:** test names should read like tree leaves and scenarios. -### When writing tests -- Write comprehensive tests for all functionality -- Test edge cases and error conditions -- Use descriptive test names -- Follow the existing test patterns in the codebase -- Include gas optimization tests where relevant +## Test Layout -### Running Tests -```bash -# Run all tests -forge test +```text +test/ +├── Base.t.sol # shared base setup (users/defaults) +├── trees/ # behaviour specifications +│ ├── ERC20.tree +│ ├── ERC721.tree +│ ├── ERC1155.tree +│ ├── ERC6909.tree +│ ├── AccessControl.tree +│ ├── Owner.tree +│ ├── ERC165.tree +│ ├── Royalty.tree +│ └── NonReentrancy.tree +├── unit/ +│ ├── token/ +│ ├── access/ +│ ├── interfaceDetection/ +│ └── libraries/ +└── utils/ + ├── harnesses/ # expose module/internal behaviour for tests + └── storage/ # vm.load/vm.store storage utilities +``` -# Run tests with verbose output -forge test -vvv +Within each feature area, tests are usually split into: -# Run specific test file -forge test --match-path test/ERC20.sol +- `*FacetBase.t.sol` / `*ModBase.t.sol` for setup and deployment. +- `facet/fuzz/*.t.sol` for facet-facing behaviour. +- `mod/fuzz/*.t.sol` for module/modifier-facing behaviour. -# Run tests with gas reporting -forge test --gas-report -``` +## How to Add or Update Tests -### Test Structure +### 1) Start from the tree -Tests in Compose follow a specific organizational pattern: +Find the relevant tree file (for example `test/trees/ERC20.tree`) and identify the branch/leaves your change affects. -- **Facet Tests** (`test/[Feature]/[Feature]Facet.t.sol`): Test external functions of facets -- **Library Tests** (`test/[Feature]/Lib[Feature].t.sol`): Test internal library functions -- **Test Harnesses** (`test/[Feature]/harnesses/`): Special contracts that expose internal functions for testing - - Facet harnesses add initialization and helper functions - - Library harnesses expose internal functions as external +If behaviour is new, add the branch/leaves first. -Example structure: -``` -test/ -├── ERC20/ -│ ├── ERC20Facet.t.sol # Tests for facet external functions -│ ├── LibERC20.t.sol # Tests for library internal functions -│ └── harnesses/ -│ ├── ERC20FacetHarness.sol # Adds mint() and initialize() -│ └── LibERC20Harness.sol # Exposes internal functions -``` +### 2) Reuse existing fixture layers -See [test/README.md](https://github.com/Perfect-Abstractions/Compose/blob/main/test/README.md) for detailed testing documentation and patterns. +- Extend the nearest domain base test (`*Base.t.sol`) rather than building setup from scratch. +- Prefer existing users/defaults/constants from `test/Base.t.sol` and `test/utils/*`. +- Label deployed contracts in setup (`vm.label`) for readable traces. -## Available Commands +### 3) Keep tests tree-aligned -### Build +- Add the BTT reference comment at the top of test contracts: -```bash -forge build +```solidity +/** + * @dev BTT spec: test/trees/ERC20.tree + */ ``` -### Test +- Use naming patterns that match behaviour: + - `test_FunctionName_Scenario` + - `test_RevertWhen_Condition` + - `testFuzz_FunctionName_Scenario` + - `testFuzz_ShouldRevert_Reason` -```bash -# Run all tests -forge test +### 4) Prefer robust fuzz setup -# Run tests with verbose output -forge test -vvv +- Use `bound(...)` for numeric ranges. +- Use `vm.assume(...)` only when needed for discrete constraints. +- Assert custom errors with selector-encoded revert data. +- Assert events with `vm.expectEmit` when the behaviour specifies emissions. -# Run specific test file -forge test --match-path test/ERC20.sol +### 5) Use harnesses and storage utils (not production hooks) -# Run tests with gas reporting -forge test --gas-report -``` +- Use `test/utils/harnesses/**` to expose internals or module surfaces in tests. +- Use `test/utils/storage/**` (`vm.load` / `vm.store`) for layout-sensitive setup/assertions. +- Do not add test-only getters or hooks to `src/`. -### Format +## Commands ```bash -forge fmt -``` +# run entire suite +forge test -### Gas Snapshots +# run one area while iterating +forge test --match-path "test/unit/token/ERC20/**" -```bash -forge snapshot -``` +# run one specific test file +forge test --match-path "test/unit/token/ERC20/Transfer/facet/fuzz/transfer.t.sol" -### Local Development +# run one specific test function +forge test --match-test "test_RevertWhen_TransferInsufficientBalance" -```bash -# Start local node -anvil +# optional detailed logs while debugging +forge test -vvv -# Interact with contracts -cast +# gas regression checks where relevant +forge test --gas-report ``` -### Help +## Pre-PR Checklist -```bash -# Get help for any command -forge --help -anvil --help -cast --help -``` \ No newline at end of file +- Behaviour tree updated (if behaviour changed). +- Tests added/updated for both success and revert paths. +- No test-only logic added to production contracts. +- `forge test` passes locally. +- `forge fmt --check` is clean. + +For deeper reference and examples, see `test/README.md` in the repository root. \ No newline at end of file