Skip to content

docs: Enhance TESTING.md with comprehensive testing guidelines and be… #5589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
113 changes: 113 additions & 0 deletions test/TESTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,116 @@
## Testing

Unit tests are critical to OpenZeppelin Contracts. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory.

### Testing Framework

OpenZeppelin Contracts uses [Hardhat](https://hardhat.org/) as its development and testing environment, along with [Mocha](https://mochajs.org/) as the test runner and [Chai](https://www.chaijs.com/) for assertions.

### Running Tests

To run the entire test suite:

```bash
npm test
```

To run a specific test file:

```bash
npx hardhat test test/path/to/test-file.js
```

To run tests with gas reporting:

```bash
REPORT_GAS=true npm test
```

### Test Structure

Tests are organized to mirror the contract directory structure. For example:
- Tests for `contracts/token/ERC20/ERC20.sol` are in `test/token/ERC20/ERC20.test.js`
- Tests for `contracts/access/Ownable.sol` are in `test/access/Ownable.test.js`

### Testing Conventions

When writing tests, follow these conventions:

1. **Descriptive Names**: Test descriptions should clearly state what is being tested.
```javascript
it('reverts when transferring tokens to the zero address', async function () { ... });
```

2. **Isolation**: Each test should be independent and not rely on the state of other tests.

3. **Complete Coverage**: Aim for 100% code coverage, testing both the normal execution paths and edge cases.

4. **Testing Modifiers**: When testing modifiers, ensure that both success and failure cases are tested.

5. **Event Testing**: Verify that events are emitted with the correct parameters.

6. **Gas Efficiency**: Consider using the gas reporter to monitor the gas costs of functions.

### Fixtures and Utilities

The `test/helpers` directory contains reusable testing utilities, including:

- Fixtures for common test setups
- Helper functions for frequently used test operations
- Custom matchers for common assertions

### Best Practices

1. **Test State Changes**: Verify that state variables are modified correctly.

2. **Test Reverts**: Use `expectRevert` to test functions that should revert in specific circumstances.
```javascript
await expectRevert(
token.transfer(constants.ZERO_ADDRESS, 100),
'ERC20: transfer to the zero address'
);
```

3. **Test Access Control**: Ensure that functions properly restrict access when required.

4. **Test Upgrade Paths**: For upgradeable contracts, test that the upgrade path works correctly.

### Continuous Integration

All tests run on GitHub Actions for each pull request. PRs will not be merged if tests fail.

### Coverage

Test coverage is tracked using [solidity-coverage](https://github.com/sc-forks/solidity-coverage). Run the following command to generate a coverage report:

```bash
npm run coverage
```

### Contributing Tests

When contributing new features or fixing bugs:

1. Always add tests that cover the changes
2. Make sure all existing tests pass
3. Consider edge cases and error conditions
4. Update test documentation if necessary

### Snapshot Testing

For complex data structures or large state changes, consider using snapshot testing to compare expected and actual values.

### Security-Specific Testing

For security-critical functions, consider adding specialized tests:

1. **Fuzzing**: Use tools like Echidna or Foundry to fuzz test inputs
2. **Invariant Testing**: Identify and test invariants that should always hold
3. **Scenario Testing**: Test full user interaction flows

### Resources

For more information on testing smart contracts, refer to:
- [Hardhat Documentation](https://hardhat.org/getting-started/)
- [OpenZeppelin Test Helpers](https://docs.openzeppelin.com/test-helpers)
- [Effective Smart Contract Testing](https://blog.openzeppelin.com/effective-smart-contract-testing/)
Loading