Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 83 additions & 77 deletions website/docs/contribution/testing.mdx
Original file line number Diff line number Diff line change
@@ -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 <subcommand>
# 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
```
- 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.
Loading