Integration tests for functions in the features folder, running on a forked blockchain using @viem/anvil and Vitest.
Create a .env.test file in the project root with the following variables:
Note: Integration tests look for environment variables in the
.env.testfile. If the file is not found,.envis used as a fallback.
DEPLOYED=deployed-hoodi-vaults.json
CHAIN_ID=560048
EL_URL=
PRIVATE_KEY=0x...
VAULT_ADDRESS=0x...
FORK_BLOCK_NUMBER=18000000
ANVIL_PORT=8545DEPLOYED- filename in theconfigs/folder with contract addressesCHAIN_ID- network ID for the forkEL_URL- RPC node URL for forkingPRIVATE_KEY- private key for executing transactionsVAULT_ADDRESS- vault address for testingFORK_BLOCK_NUMBER- (optional) block number for forkingANVIL_PORT- (optional) port for Anvil (default: 8545)
# Run all integration tests
yarn test:integration
# Run in watch mode
yarn test:integration:watch
# Run all tests (unit + integration)
yarn test
# Run with code coverage
yarn test:coverageglobalSetup.ts- starts Anvil fork before tests and stops after completionhelpers/- testing utilities:test-config.ts- configuration loading from envtest-client.ts- client creation and Anvil utilitiestest-assertions.ts- helper functions for assertions and mocking
*.test.ts- tests for various functions
Tests use Vitest instead of Jest. Main differences:
- Imports from
vitestinstead of@jest/globals - Mocks via
viinstead ofjest - Faster execution and better ESM support
Integration tests automatically skip all interactive prompts. When functions from features call confirmOperation(), in the test environment the prompt is automatically skipped without blocking execution.
This works through environment variables:
NODE_ENV=testVITEST=true
Available utilities for testing write operations and manipulating time/blocks:
import { createAnvilTestClient } from './helpers/test-client.js';
import { mainnet } from 'viem/chains';
const testClient = createAnvilTestClient(mainnet, 'http://127.0.0.1:8545');import { impersonateAccount } from './helpers/test-client.js';
import { parseEther } from 'viem';
// Impersonate with 100 ETH balance by default
await impersonateAccount(testClient, address);
// Impersonate with custom balance
await impersonateAccount(testClient, address, parseEther('1000'));import { mintEth } from './helpers/test-client.js';
import { parseEther } from 'viem';
// Set balance for an address
await mintEth(testClient, address, parseEther('1000'));import {
increaseTime,
setTime,
getCurrentTimestamp,
} from './helpers/test-client.js';
// Get current timestamp
const currentTime = await getCurrentTimestamp(testClient);
// Increase time by 1 hour (3600 seconds)
await increaseTime(testClient, 3600);
// Set specific time
await setTime(testClient, BigInt(Date.now() / 1000));import { mineBlocks } from './helpers/test-client.js';
// Mine 1 block
await mineBlocks(testClient);
// Mine 5 blocks
await mineBlocks(testClient, 5);import { getAccountFromPrivateKey } from './helpers/test-client.js';
const account = getAccountFromPrivateKey('0x...');
// Used for creating wallet clients