Build comprehensive testing frameworks for smart contracts including unit tests, integration tests, and fuzzing.
## ROLE
You are a smart contract QA engineer who has built testing suites for protocols with millions in TVL.
## CONTEXT
I need to create a testing framework for my smart contracts.
## CONTRACT INFO
- Contract type: ${{TYPE}}
- Key functions: ${{FUNCTIONS}}
- External dependencies: ${{DEPENDENCIES}}
- Coverage target: ${{COVERAGE}}
## TASK
Create comprehensive testing framework:
**1. TEST STRUCTURE**
```
test/
├── unit/
│ ├── Token.test.js
│ └── Staking.test.js
├── integration/
│ └── FullFlow.test.js
├── fuzz/
│ └── Fuzz.test.js
└── helpers/
└── setup.js
```
**2. UNIT TEST EXAMPLE (HARDHAT)**
```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
describe("Token", function () {
async function deployFixture() {
const [owner, user1, user2] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const token = await Token.deploy(owner.address);
return { token, owner, user1, user2 };
}
describe("Deployment", function () {
it("Should set the right owner", async function () {
const { token, owner } = await loadFixture(deployFixture);
expect(await token.owner()).to.equal(owner.address);
});
it("Should assign total supply to owner", async function () {
const { token, owner } = await loadFixture(deployFixture);
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
});
});
describe("Transfers", function () {
it("Should transfer tokens between accounts", async function () {
const { token, owner, user1 } = await loadFixture(deployFixture);
await token.transfer(user1.address, 100);
expect(await token.balanceOf(user1.address)).to.equal(100);
});
it("Should fail if sender doesn't have enough tokens", async function () {
const { token, user1, user2 } = await loadFixture(deployFixture);
await expect(
token.connect(user1).transfer(user2.address, 100)
).to.be.revertedWithCustomError(token, "InsufficientBalance");
});
it("Should emit Transfer event", async function () {
const { token, owner, user1 } = await loadFixture(deployFixture);
await expect(token.transfer(user1.address, 100))
.to.emit(token, "Transfer")
.withArgs(owner.address, user1.address, 100);
});
});
});
```
**3. FOUNDRY TESTS**
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Token.sol";
contract TokenTest is Test {
Token public token;
address public owner = address(1);
address public user = address(2);
function setUp() public {
vm.prank(owner);
token = new Token(owner);
}
function testTransfer() public {
vm.prank(owner);
token.transfer(user, 100);
assertEq(token.balanceOf(user), 100);
}
function testFailTransferInsufficientBalance() public {
vm.prank(user);
token.transfer(owner, 100); // Should fail
}
function testFuzz_Transfer(uint256 amount) public {
vm.assume(amount <= token.balanceOf(owner));
vm.prank(owner);
token.transfer(user, amount);
assertEq(token.balanceOf(user), amount);
}
}
```
**4. INTEGRATION TESTS**
```javascript
describe("Full Flow Integration", function () {
it("Should complete full staking cycle", async function () {
// Deploy all contracts
const token = await deployToken();
const staking = await deployStaking(token.address);
// Fund staking with rewards
await token.transfer(staking.address, ethers.parseEther("1000"));
// User stakes
await token.connect(user).approve(staking.address, ethers.parseEther("100"));
await staking.connect(user).stake(ethers.parseEther("100"));
// Time passes
await time.increase(86400); // 1 day
// User claims rewards
const rewardBefore = await token.balanceOf(user.address);
await staking.connect(user).claimReward();
const rewardAfter = await token.balanceOf(user.address);
expect(rewardAfter).to.be.gt(rewardBefore);
// User withdraws
await staking.connect(user).withdraw(ethers.parseEther("100"));
expect(await staking.balanceOf(user.address)).to.equal(0);
});
});
```
**5. FUZZ TESTING**
```javascript
describe("Fuzz Tests", function () {
it("Should never lose tokens", async function () {
for (let i = 0; i < 100; i++) {
const randomAmount = BigInt(Math.floor(Math.random() * 1000000));
const randomAddress = ethers.Wallet.createRandom().address;
const totalBefore = await token.totalSupply();
// Perform random operations
const totalAfter = await token.totalSupply();
expect(totalAfter).to.equal(totalBefore);
}
});
});
```
**6. GAS REPORTING**
```javascript
// hardhat.config.js
module.exports = {
gasReporter: {
enabled: true,
currency: 'USD',
coinmarketcap: API_KEY,
}
};
```
**7. COVERAGE COMMANDS**
```bash
# Hardhat
npx hardhat coverage
# Foundry
forge coverage
```
**8. TEST CHECKLIST**
| Category | Tests | Coverage |
|----------|-------|----------|
| Happy paths | | |
| Edge cases | | |
| Reverts | | |
| Access control | | |
| Events | | |
| Gas limits | | |Or press ⌘C to copy
Replace these placeholders with your own content before using the prompt.
[{TYPE][{FUNCTIONS][{DEPENDENCIES][{COVERAGE]{ expect }{ ethers }{ loadFixture }{
async function deployFixture() {
const [owner, user1, user2] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const token = await Token.deploy(owner.address);
return { token, owner, user1, user2 }{
it("Should set the right owner", async function () {
const { token, owner }{
const { token, owner }{
it("Should transfer tokens between accounts", async function () {
const { token, owner, user1 }{
const { token, user1, user2 }{
const { token, owner, user1 }{
Token public token;
address public owner = address(1);
address public user = address(2);
function setUp() public {
vm.prank(owner);
token = new Token(owner);
}{
vm.prank(owner);
token.transfer(user, 100);
assertEq(token.balanceOf(user), 100);
}{
vm.prank(user);
token.transfer(owner, 100); // Should fail
}{
vm.assume(amount <= token.balanceOf(owner));
vm.prank(owner);
token.transfer(user, amount);
assertEq(token.balanceOf(user), amount);
}{
it("Should complete full staking cycle", async function () {
// Deploy all contracts
const token = await deployToken();
const staking = await deployStaking(token.address);
// Fund staking with rewards
await token.transfer(staking.address, ethers.parseEther("1000"));
// User stakes
await token.connect(user).approve(staking.address, ethers.parseEther("100"));
await staking.connect(user).stake(ethers.parseEther("100"));
// Time passes
await time.increase(86400); // 1 day
// User claims rewards
const rewardBefore = await token.balanceOf(user.address);
await staking.connect(user).claimReward();
const rewardAfter = await token.balanceOf(user.address);
expect(rewardAfter).to.be.gt(rewardBefore);
// User withdraws
await staking.connect(user).withdraw(ethers.parseEther("100"));
expect(await staking.balanceOf(user.address)).to.equal(0);
}{
it("Should never lose tokens", async function () {
for (let i = 0; i < 100; i++) {
const randomAmount = BigInt(Math.floor(Math.random() * 1000000));
const randomAddress = ethers.Wallet.createRandom().address;
const totalBefore = await token.totalSupply();
// Perform random operations
const totalAfter = await token.totalSupply();
expect(totalAfter).to.equal(totalBefore);
}{
gasReporter: {
enabled: true,
currency: 'USD',
coinmarketcap: API_KEY,
}