Testing¶
Comprehensive testing guide for Prediction DAO smart contracts and frontend.
Test Structure¶
test/
├── WelfareMetricRegistry.test.js
├── ProposalRegistry.test.js
├── ConditionalMarketFactory.test.js
├── PrivacyCoordinator.test.js
├── OracleResolver.test.js
├── RagequitModule.test.js
└── FutarchyGovernor.test.js
Running Tests¶
# Run all tests
npm test
# Run specific test file
npx hardhat test test/ProposalRegistry.test.js
# Run with coverage
npm run test:coverage
# Run with gas reporting
npx hardhat test --gas-reporter
Writing Tests¶
Basic Test Structure¶
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
describe("ProposalRegistry", function () {
// Fixture for reusable setup
async function deployFixture() {
const [owner, proposer, other] = await ethers.getSigners();
const ProposalRegistry = await ethers.getContractFactory("ProposalRegistry");
const proposalRegistry = await ProposalRegistry.deploy();
await proposalRegistry.waitForDeployment();
return { proposalRegistry, owner, proposer, other };
}
describe("Proposal Submission", function () {
it("Should submit proposal with correct bond", async function () {
const { proposalRegistry, proposer } = await loadFixture(deployFixture);
const tx = await proposalRegistry.connect(proposer).submitProposal(
"Test Proposal",
"Description",
ethers.parseEther("1000"),
proposer.address,
1,
{ value: ethers.parseEther("50") }
);
await expect(tx)
.to.emit(proposalRegistry, "ProposalSubmitted")
.withArgs(0, proposer.address);
});
it("Should revert if bond is insufficient", async function () {
const { proposalRegistry, proposer } = await loadFixture(deployFixture);
await expect(
proposalRegistry.connect(proposer).submitProposal(
"Test",
"Description",
ethers.parseEther("1000"),
proposer.address,
1,
{ value: ethers.parseEther("40") } // Too low
)
).to.be.revertedWith("Insufficient bond");
});
});
});
Testing Events¶
it("Should emit correct event", async function () {
const tx = await contract.doSomething();
await expect(tx)
.to.emit(contract, "SomethingDone")
.withArgs(expectedArg1, expectedArg2);
});
Testing Reverts¶
it("Should revert with correct message", async function () {
await expect(
contract.restrictedFunction()
).to.be.revertedWith("Not authorized");
// For custom errors
await expect(
contract.restrictedFunction()
).to.be.revertedWithCustomError(contract, "Unauthorized");
});
Testing State Changes¶
it("Should update state correctly", async function () {
await contract.updateValue(42);
expect(await contract.value()).to.equal(42);
});
Test Coverage Goals¶
Aim for comprehensive coverage:
- Statements: > 95%
- Branches: > 90%
- Functions: > 95%
- Lines: > 95%
Check coverage:
Integration Tests¶
Test contract interactions:
describe("Proposal Lifecycle", function () {
it("Should complete full proposal flow", async function () {
// Setup
const { futarchyGovernor, proposalRegistry, marketFactory } =
await loadFixture(deploySystemFixture);
// Submit proposal
await proposalRegistry.submitProposal(...);
// Activate
await futarchyGovernor.activateProposal(0);
// Verify market created
const marketId = await marketFactory.getMarketForProposal(0);
expect(marketId).to.not.equal(0);
// Trade
// ... trade on market
// Resolve
await oracleResolver.submitReport(...);
// Execute
await futarchyGovernor.executeProposal(0);
// Verify execution
const proposal = await proposalRegistry.getProposal(0);
expect(proposal.status).to.equal(ProposalStatus.Executed);
});
});
Gas Optimization Tests¶
it("Should use reasonable gas", async function () {
const tx = await contract.expensiveOperation();
const receipt = await tx.wait();
expect(receipt.gasUsed).to.be.lessThan(300000);
});
Security Tests¶
Test for common vulnerabilities:
describe("Security", function () {
it("Should prevent reentrancy", async function () {
const Attacker = await ethers.getContractFactory("ReentrancyAttacker");
const attacker = await Attacker.deploy(contract.target);
await expect(
attacker.attack()
).to.be.reverted;
});
it("Should enforce access control", async function () {
const { contract, unauthorized } = await loadFixture(deployFixture);
await expect(
contract.connect(unauthorized).privilegedFunction()
).to.be.revertedWith("Not authorized");
});
});
Frontend Testing¶
See frontend/ directory for React component tests.
Continuous Integration¶
Tests run automatically on every commit via GitHub Actions.
For more details, see: