Running Tests
- All Tests
- Specific Test
- With Coverage
- CI Mode
Run the complete test suite:Expected output:
yarn test
DeBridgePipeline
✓ Should send native asset (432ms)
✓ Should send ERC20 token (523ms)
✓ Should claim transfer (891ms)
...
85 passing (2m 14s)
Run a single test file:
yarn hardhat test test/05_DebridgePipeline.test.js
Generate code coverage report:Creates a coverage report in
yarn coverage
coverage/ directory.Run in CI environment:Compiles contracts first, then runs tests.
yarn test:ci
Test Structure
Tests are organized in thetest/ directory:
test/
├── 00_CallProxy.test.js # CallProxy unit tests
├── 00_SignatureVerifier.test.js # Signature verification tests
├── 02_LightDebridge.test.js # Basic bridge functionality
├── 05_DebridgePipeline.test.js # Full integration tests
├── SimpleFeeProxy.test.ts # Fee proxy tests
└── utils/ # Test utilities
├── signature.js
├── helpers.js
└── constants.js
Writing Tests
Basic Test Template
Basic Test Structure
const { expect } = require('chai');
const { ethers } = require('hardhat');
describe('MyContract', function () {
let myContract;
let owner, user1, user2;
beforeEach(async function () {
// Get signers
[owner, user1, user2] = await ethers.getSigners();
// Deploy contract
const MyContract = await ethers.getContractFactory('MyContract');
myContract = await MyContract.deploy();
await myContract.deployed();
});
it('Should perform action correctly', async function () {
// Arrange
const value = 100;
// Act
await myContract.connect(user1).doSomething(value);
// Assert
expect(await myContract.getValue()).to.equal(value);
});
});
Testing DeBridgeGate
DeBridgeGate Test Example
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
describe('DeBridgeGate Send Function', function () {
let debridgeGate, token;
let owner, user, receiver;
beforeEach(async function () {
[owner, user, receiver] = await ethers.getSigners();
// Deploy WETH
const WETH = await ethers.getContractFactory('WETH');
const weth = await WETH.deploy();
// Deploy DeBridgeGate as upgradeable
const DeBridgeGate = await ethers.getContractFactory('DeBridgeGate');
debridgeGate = await upgrades.deployProxy(
DeBridgeGate,
[5, weth.address], // excessConfirmations, weth
{ initializer: 'initialize' }
);
// Deploy test ERC20
const Token = await ethers.getContractFactory('MockERC20');
token = await Token.deploy('Test Token', 'TEST', 18);
await token.mint(user.address, ethers.utils.parseEther('1000'));
});
it('Should send native token', async function () {
const amount = ethers.utils.parseEther('1');
const chainIdTo = 56; // BSC
// Get protocol fee
const fee = await debridgeGate.globalFixedNativeFee();
// Send native token
const tx = await debridgeGate.connect(user).send(
ethers.constants.AddressZero, // native token
amount,
chainIdTo,
ethers.utils.hexZeroPad(receiver.address, 32),
'0x',
false,
0,
'0x',
{ value: amount.add(fee) }
);
// Check event
await expect(tx)
.to.emit(debridgeGate, 'Sent')
.withArgs(
/* check event args */
);
});
it('Should send ERC20 token', async function () {
const amount = ethers.utils.parseEther('100');
const chainIdTo = 137; // Polygon
// Approve
await token.connect(user).approve(debridgeGate.address, amount);
// Get fee
const fee = await debridgeGate.globalFixedNativeFee();
// Send token
await expect(
debridgeGate.connect(user).send(
token.address,
amount,
chainIdTo,
ethers.utils.hexZeroPad(receiver.address, 32),
'0x',
false,
0,
'0x',
{ value: fee }
)
).to.emit(debridgeGate, 'Sent');
// Check balance
expect(await token.balanceOf(user.address))
.to.equal(ethers.utils.parseEther('900'));
});
});
Testing Oracle Signatures
Signature Verification Test
const { signSubmission } = require('./utils/signature');
it('Should verify oracle signatures', async function () {
// Setup oracles
const oracles = [
await ethers.getSigner(0),
await ethers.getSigner(1),
await ethers.getSigner(2)
];
// Add oracles
await oraclesManager.addOracles(
oracles.map(o => o.address),
[true, true, true] // all required
);
// Create submission data
const submissionId = ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
['uint256', 'uint256', 'bytes32'],
[chainIdFrom, nonce, debridgeId]
)
);
// Sign with oracles
const signatures = await Promise.all(
oracles.map(oracle => signSubmission(submissionId, oracle))
);
// Concatenate signatures
const concatenatedSigs = ethers.utils.concat(signatures);
// Verify
const [count] = await signatureVerifier.submit(
submissionId,
concatenatedSigs
);
expect(count).to.equal(3);
});
Test Utilities
Common Helpers
Test Helpers
// Time manipulation
const { time } = require('@openzeppelin/test-helpers');
await time.increase(3600); // Advance 1 hour
await time.increaseTo(futureTimestamp);
// Balance tracking
const { balance } = require('@openzeppelin/test-helpers');
const tracker = await balance.tracker(user.address);
await someTransaction();
const delta = await tracker.delta();
expect(delta).to.equal(expectedChange);
// Expectation helpers
const { expectRevert, expectEvent } = require('@openzeppelin/test-helpers');
await expectRevert(
contract.failingFunction(),
'Expected error message'
);
const receipt = await contract.successFunction();
await expectEvent(receipt, 'EventName', {
param1: expectedValue1,
param2: expectedValue2
});
Coverage
Generate and view coverage:# Run coverage
yarn coverage
# Open HTML report
open coverage/index.html
- Statements: > 90%
- Branches: > 85%
- Functions: > 90%
- Lines: > 90%
Focus on covering critical paths: send, claim, signature verification, and fee calculations.
Debugging Tests
Console Logging
// In Solidity contracts
console.log('Debug value:', someValue);
// In tests
console.log('Address:', await contract.address);
console.log('Balance:', (await token.balanceOf(user)).toString());
Hardhat Network Logging
hardhat.config.ts
module.exports = {
networks: {
hardhat: {
loggingEnabled: true
}
}
};
Gas Reporting
# Enable gas reporting
REPORT_GAS=true yarn test
CI/CD Integration
GitHub Actions example:.github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: yarn install
- run: yarn test:ci
- run: yarn coverage
- uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
Best Practices
Related Documentation
Development Setup
Configure your development environment
Deployment
Deploy contracts to networks