4.11 测试运维
一句话总结:测试保证质量,运维保证稳定——上线前的最后一道防线。
📊 学习进度
- 状态:⬜ 未开始
- 预计时长:4-5 小时
- 已完成:0/3 个模块
- 在整体流程中的位置:预测市场实战·第 8 阶段
📍 本章定位
- 服务方案:方案 3(核心 90%)
- 学习方式:🔥 推荐
- 在流程中的作用:测试、部署、监控
- 核心知识点:自动化测试、CI/CD、监控告警
- 预计时长:4-5 小时
- 完成后能做什么:能完成测试和部署
人机分工
| 环节 | 谁做 | 重要度 | 说明 |
|---|---|---|---|
| 测试策略 | 🧑 人 | ⭐⭐⭐⭐⭐ | 决定测试什么 |
| 测试用例生成 | 🤖 AI | ⭐⭐⭐⭐ | AI 生成测试 |
| 测试执行 | 🤖 AI | ⭐⭐⭐⭐ | 自动化执行 |
| 部署策略 | 🧑 人 | ⭐⭐⭐⭐⭐ | 决定部署方案 |
| 监控配置 | 🤖 AI | ⭐⭐⭐ | AI 配置 |
1. 测试策略
1.1 测试金字塔
| 测试类型 | 覆盖范围 | 工具 | 成本 | 占比 |
|---|---|---|---|---|
| 单元测试 | 单个函数/合约 | Hardhat/Foundry | 低 | 70% |
| 集成测试 | 模块间交互 | 自定义脚本 | 中 | 20% |
| 端到端测试 | 完整用户流程 | Playwright | 高 | 10% |
| 压力测试 | 高并发场景 | k6/Artillery | 中 | - |
| 安全审计 | 漏洞检测 | Slither + AI | 高 | - |
1.2 测试覆盖率目标
| 模块 | 目标覆盖率 | 说明 |
|---|---|---|
| 智能合约 | 95%+ | 核心逻辑,必须高覆盖 |
| 撮合引擎 | 90%+ | 交易核心 |
| API 层 | 85%+ | 接口层 |
| 前端 | 70%+ | UI 层 |
| 做市系统 | 80%+ | 策略逻辑 |
1.3 测试策略流程图
2. 智能合约测试
2.1 单元测试
typescript
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
describe("EventFactory", function () {
// 测试夹具:部署合约
async function deployFixture() {
const [owner, operator, user] = await ethers.getSigners();
const EventPod = await ethers.getContractFactory("EventPod");
const eventPod = await EventPod.deploy();
const EventFactory = await ethers.getContractFactory("EventFactory");
const factory = await EventFactory.deploy(eventPod.address);
// 授权 operator
await factory.grantRole(await factory.OPERATOR_ROLE(), operator.address);
return { factory, eventPod, owner, operator, user };
}
describe("createEvent", function () {
it("should create event successfully", async function () {
const { factory, operator } = await loadFixture(deployFixture);
const tx = await factory.connect(operator).createEvent(
"BTC will reach $150k by 2026",
"Bitcoin price prediction",
"crypto",
"CoinGecko",
Math.floor(Date.now() / 1000) + 86400 * 30 // 30 days
);
const receipt = await tx.wait();
expect(receipt.status).to.equal(1);
const eventCount = await factory.eventCount();
expect(eventCount).to.equal(1);
});
it("should revert if not operator", async function () {
const { factory, user } = await loadFixture(deployFixture);
await expect(
factory.connect(user).createEvent(
"Test",
"Test",
"test",
"test",
Math.floor(Date.now() / 1000) + 86400
)
).to.be.revertedWith("AccessControl");
});
it("should revert if end time is in the past", async function () {
const { factory, operator } = await loadFixture(deployFixture);
await expect(
factory.connect(operator).createEvent(
"Test",
"Test",
"test",
"test",
Math.floor(Date.now() / 1000) - 86400 // past
)
).to.be.revertedWith("End time must be in the future");
});
});
describe("resolveEvent", function () {
it("should resolve event correctly", async function () {
const { factory, operator } = await loadFixture(deployFixture);
// 创建事件
await factory.connect(operator).createEvent(
"Test Event",
"Test",
"test",
"test",
Math.floor(Date.now() / 1000) + 86400
);
// 解决事件
await factory.connect(operator).resolveEvent(0, true);
// 验证结果
const eventInfo = await factory.getEventInfo(0);
expect(eventInfo.resolved).to.be.true;
expect(eventInfo.outcome).to.be.true;
});
});
});2.2 集成测试
typescript
describe("Full Flow Integration", function () {
it("should complete full prediction market flow", async function () {
// 1. 部署所有合约
const { factory, orderBook, oracle } = await deployAllContracts();
// 2. 创建事件
await factory.createEvent("Test Event", ...);
// 3. 用户下 YES 买单
await orderBook.connect(buyer).placeOrder(true, 6000, 100);
// 4. 用户下 NO 卖单
await orderBook.connect(seller).placeOrder(false, 6000, 100);
// 5. 验证撮合
const trades = await orderBook.getTrades();
expect(trades.length).to.equal(1);
// 6. 预言机提交结果
await oracle.submitResult(0, true);
// 7. 结算
await orderBook.settle(0);
// 8. 验证用户余额
const buyerBalance = await orderBook.getBalance(buyer.address);
expect(buyerBalance).to.be.gt(0);
});
});2.3 安全测试
typescript
describe("Security Tests", function () {
it("should prevent reentrancy attack", async function () {
const { orderBook, attacker } = await loadFixture(deployFixture);
// 部署攻击合约
const AttackerContract = await ethers.getContractFactory("ReentrancyAttacker");
const attackContract = await AttackerContract.deploy(orderBook.address);
// 尝试重入攻击
await expect(
attackContract.attack()
).to.be.revertedWith("ReentrancyGuard");
});
it("should prevent integer overflow", async function () {
const { orderBook } = await loadFixture(deployFixture);
// 尝试溢出
await expect(
orderBook.placeOrder(true, ethers.MaxUint256, 1)
).to.be.reverted;
});
it("should prevent unauthorized access", async function () {
const { factory, user } = await loadFixture(deployFixture);
await expect(
factory.connect(user).resolveEvent(0, true)
).to.be.revertedWith("AccessControl");
});
});3. CI/CD 流水线
3.1 流水线架构
3.2 GitHub Actions 配置
yaml
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
- name: Run security scan
run: npm run security:scan
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
deploy-testnet:
needs: test
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to testnet
run: npm run deploy:testnet
env:
PRIVATE_KEY: ${{ secrets.TESTNET_PRIVATE_KEY }}
RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
deploy-mainnet:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to mainnet
run: npm run deploy:mainnet
env:
PRIVATE_KEY: ${{ secrets.MAINNET_PRIVATE_KEY }}
RPC_URL: ${{ secrets.MAINNET_RPC_URL }}3.3 部署脚本
typescript
// scripts/deploy.ts
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying with account:", deployer.address);
// 1. 部署 EventPod 实现
const EventPod = await ethers.getContractFactory("EventPod");
const eventPod = await EventPod.deploy();
await eventPod.waitForDeployment();
console.log("EventPod deployed:", await eventPod.getAddress());
// 2. 部署 EventFactory
const EventFactory = await ethers.getContractFactory("EventFactory");
const factory = await EventFactory.deploy(await eventPod.getAddress());
await factory.waitForDeployment();
console.log("EventFactory deployed:", await factory.getAddress());
// 3. 部署 OrderBook 实现
const OrderBook = await ethers.getContractFactory("OrderBook");
const orderBook = await OrderBook.deploy();
await orderBook.waitForDeployment();
console.log("OrderBook deployed:", await orderBook.getAddress());
// 4. 部署 OrderBookFactory
const OrderBookFactory = await ethers.getContractFactory("OrderBookFactory");
const orderBookFactory = await OrderBookFactory.deploy(await orderBook.getAddress());
await orderBookFactory.waitForDeployment();
console.log("OrderBookFactory deployed:", await orderBookFactory.getAddress());
// 5. 部署 OracleManager
const OracleManager = await ethers.getContractFactory("OracleManager");
const oracle = await OracleManager.deploy();
await oracle.waitForDeployment();
console.log("OracleManager deployed:", await oracle.getAddress());
// 6. 保存部署信息
const deployment = {
network: (await ethers.provider.getNetwork()).name,
chainId: (await ethers.provider.getNetwork()).chainId.toString(),
deployer: deployer.address,
contracts: {
eventPod: await eventPod.getAddress(),
eventFactory: await factory.getAddress(),
orderBook: await orderBook.getAddress(),
orderBookFactory: await orderBookFactory.getAddress(),
oracleManager: await oracle.getAddress()
},
timestamp: new Date().toISOString()
};
const fs = require('fs');
fs.writeFileSync(
`deployments/${deployment.network}.json`,
JSON.stringify(deployment, null, 2)
);
console.log("Deployment saved to:", `deployments/${deployment.network}.json`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});4. 监控与告警
4.1 监控指标
| 类别 | 指标 | 告警阈值 | 说明 |
|---|---|---|---|
| 系统 | API 响应时间 | >2s | 性能下降 |
| 错误率 | >1% | 系统异常 | |
| 可用性 | <99.9% | 服务中断 | |
| 交易 | 异常交易 | 金额>$10k | 可能攻击 |
| 撮合延迟 | >100ms | 性能问题 | |
| 合约 | Gas 消耗 | >阈值 | 成本异常 |
| 余额异常 | 变动>10% | 资金风险 | |
| 做市 | 库存风险 | >30% | 风险过高 |
| 价差异常 | >5% | 流动性问题 |
4.2 监控架构
4.3 Prometheus 配置
yaml
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'prediction-market-api'
static_configs:
- targets: ['api:3000']
metrics_path: '/metrics'
- job_name: 'prediction-market-matching'
static_configs:
- targets: ['matching-engine:3001']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']4.4 告警规则
yaml
# alert_rules.yml
groups:
- name: prediction-market
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
for: 2m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }}%"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "High API latency"
description: "95th percentile latency is {{ $value }}s"
- alert: LowBalance
expr: wallet_balance_usdc < 1000
for: 1m
labels:
severity: critical
annotations:
summary: "Low wallet balance"
description: "Balance is {{ $value }} USDC"4.5 Telegram 告警 Bot
typescript
import TelegramBot from 'node-telegram-bot-api';
class AlertBot {
private bot: TelegramBot;
private chatId: string;
constructor(token: string, chatId: string) {
this.bot = new TelegramBot(token, { polling: false });
this.chatId = chatId;
}
async sendAlert(alert: Alert): Promise<void> {
const message = this.formatAlert(alert);
await this.bot.sendMessage(this.chatId, message, { parse_mode: 'HTML' });
}
private formatAlert(alert: Alert): string {
const emoji = alert.severity === 'critical' ? '🚨' : '⚠️';
return `
${emoji} <b>${alert.title}</b>
${alert.description}
<b>Severity:</b> ${alert.severity}
<b>Time:</b> ${new Date(alert.timestamp).toISOString()}
<b>Service:</b> ${alert.service}
`.trim();
}
}5. 压力测试
5.1 压力测试方案
| 测试场景 | 并发用户 | 持续时间 | 目标 |
|---|---|---|---|
| 正常负载 | 100 | 10 分钟 | 系统稳定 |
| 高峰负载 | 500 | 5 分钟 | 无降级 |
| 极限负载 | 1000 | 2 分钟 | 优雅降级 |
| 持续压力 | 200 | 30 分钟 | 无内存泄漏 |
5.2 k6 压力测试脚本
javascript
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // 升压到 100 用户
{ duration: '5m', target: 100 }, // 维持 100 用户
{ duration: '2m', target: 500 }, // 升压到 500 用户
{ duration: '5m', target: 500 }, // 维持 500 用户
{ duration: '2m', target: 0 }, // 降压到 0
],
thresholds: {
http_req_duration: ['p(95)<2000'], // 95% 请求 < 2s
http_req_failed: ['rate<0.01'], // 错误率 < 1%
},
};
export default function () {
// 获取事件列表
const eventsRes = http.get('https://api.example.com/api/events');
check(eventsRes, {
'events status is 200': (r) => r.status === 200,
'events response time < 500ms': (r) => r.timings.duration < 500,
});
// 获取订单簿
const orderbookRes = http.get('https://api.example.com/api/orderbook/event-1');
check(orderbookRes, {
'orderbook status is 200': (r) => r.status === 200,
});
// 下单
const orderRes = http.post('https://api.example.com/api/orders', JSON.stringify({
eventId: 'event-1',
side: 'YES',
orderType: 'LIMIT',
price: 0.6,
amount: 100,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(orderRes, {
'order status is 200': (r) => r.status === 200,
});
sleep(1);
}6. 测试策略进阶
6.1 混沌工程
在测试网模拟各种异常场景,验证系统韧性:
| 混沌场景 | 注入方式 | 验证目标 | 恢复时间目标 |
|---|---|---|---|
| RPC 节点宕机 | 关闭主 RPC | 自动切换备用节点 | <30s |
| 数据库主从延迟 | 人为注入延迟 | 读写分离正常 | <60s |
| 网络分区 | iptables 规则 | 系统降级运行 | <120s |
| 内存压力 | stress-ng | 无 OOM Kill | 持续运行 |
| Gas 价格飙升 | 模拟高 Gas | 自动暂停链上操作 | 立即 |
混沌测试脚本:
bash
#!/bin/bash
# chaos-test.sh - 混沌测试执行脚本
echo "=== 混沌测试开始 ==="
# 场景 1: RPC 节点故障
echo "[1] 模拟主 RPC 节点宕机..."
docker stop rpc-primary
sleep 5
curl -s http://localhost:3000/health | jq '.rpc.status'
# 验证: 应该自动切换到备用节点
# 场景 2: 数据库高延迟
echo "[2] 注入数据库延迟 500ms..."
tc qdisc add dev eth0 root netem delay 500ms
sleep 30
curl -s http://localhost:3000/metrics | grep 'db_query_duration'
tc qdisc del dev eth0 root
# 场景 3: 内存压力
echo "[3] 施加内存压力..."
stress-ng --vm 2 --vm-bytes 512M --timeout 60s &
sleep 65
docker stats --no-stream | grep 'prediction-market'
echo "=== 混沌测试完成 ==="6.2 合约模糊测试
使用 Foundry 的模糊测试发现边界情况:
solidity
// test/FuzzTest.t.sol
contract OrderBookFuzzTest is Test {
OrderBook orderBook;
function setUp() public {
orderBook = new OrderBook();
}
/**
* 模糊测试:随机价格和数量
*/
function testFuzz_placeOrder(
uint256 price,
uint256 quantity,
bool isBuy
) public {
// 约束有效范围
price = bound(price, 1, 99);
quantity = bound(quantity, 1, 10000);
// 不应该 revert(除非余额不足)
vm.assume(quantity * price <= 1_000_000);
// 执行下单
orderBook.placeOrder(isBuy, price, quantity);
// 验证不变量
assert(orderBook.totalBuyVolume() + orderBook.totalSellVolume()
<= orderBook.totalLiquidity());
}
/**
* 模糊测试:撮合不变量
*/
function testFuzz_matchingInvariant(
uint256 buyPrice,
uint256 sellPrice,
uint256 quantity
) public {
buyPrice = bound(buyPrice, 1, 99);
sellPrice = bound(sellPrice, 1, 99);
quantity = bound(quantity, 1, 1000);
// 下买单
orderBook.placeOrder(true, buyPrice, quantity);
// 下卖单
orderBook.placeOrder(false, sellPrice, quantity);
// 如果买价 >= 卖价,应该有成交
if (buyPrice >= sellPrice) {
assert(orderBook.tradeCount() > 0);
}
}
}6.3 端到端测试场景
typescript
// e2e/full-flow.spec.ts
import { test, expect } from '@playwright/test';
test.describe('预测市场完整流程', () => {
test('用户从连接钱包到完成交易', async ({ page }) => {
// 1. 访问首页
await page.goto('/');
await expect(page.locator('h1')).toContainText('预测市场');
// 2. 连接钱包
await page.click('[data-testid="connect-wallet"]');
await page.click('[data-testid="metamask"]');
// Mock MetaMask 连接
await page.waitForSelector('[data-testid="wallet-connected"]');
// 3. 浏览事件
await page.click('[data-testid="event-card"]:first-child');
await expect(page.locator('[data-testid="probability"]')).toBeVisible();
// 4. 下单交易
await page.fill('[data-testid="amount-input"]', '100');
await page.click('[data-testid="buy-yes"]');
await page.click('[data-testid="confirm-order"]');
// 5. 验证订单
await page.waitForSelector('[data-testid="order-confirmed"]');
const orderStatus = await page.textContent('[data-testid="order-status"]');
expect(orderStatus).toContain('已成交');
// 6. 查看持仓
await page.click('[data-testid="my-positions"]');
await expect(page.locator('[data-testid="position-row"]')).toHaveCount(1);
});
});7. 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 测试通过但线上出问题 | 环境差异 | 测试网模拟真实环境 |
| 部署失败 | 合约升级冲突 | 使用代理模式 |
| 监控漏报 | 阈值不当 | 调整阈值+增加维度 |
| 告警疲劳 | 告警太多 | 分级告警+聚合 |
| 回滚困难 | 没有回滚方案 | 代理模式+多签 |
| 测试覆盖不足 | 时间不够 | AI 生成测试用例 |
7. 下一步
完成测试运维后,进入 阶段 9:主网上线
6.4 合约形式化验证
形式化验证是智能合约安全的最高保障,通过数学证明确保合约行为符合预期:
solidity
// 使用 Certora 进行形式化验证
// certora/OrderBook.spec
/*
规则 1: 买单成交后,买家余额减少
*/
rule buyDecreasesBuyerBalance {
address buyer;
uint256 amount;
env e;
require e.msg.sender == buyer;
uint256 balanceBefore = balanceOf(buyer);
placeBuyOrder(e, amount);
uint256 balanceAfter = balanceOf(buyer);
assert balanceAfter <= balanceBefore,
"Buy order should decrease or maintain buyer balance";
}
/*
规则 2: 总供应量守恒
*/
rule totalSupplyConserved {
uint256 totalBefore = totalSupply();
env e;
method f;
calldataarg args;
f(e, args);
uint256 totalAfter = totalSupply();
assert totalAfter == totalBefore,
"Total supply should be conserved";
}
/*
规则 3: 结算后赢家获得全部资金
*/
rule winnerTakesAll {
uint256 eventId;
address winner;
env e;
uint256 poolBefore = getPoolBalance(eventId);
uint256 winnerBefore = balanceOf(winner);
settle(e, eventId, true);
claim(e, eventId);
uint256 winnerAfter = balanceOf(winner);
assert winnerAfter - winnerBefore == poolBefore,
"Winner should receive entire pool";
}| 验证工具 | 适用场景 | 学习曲线 | 费用 |
|---|---|---|---|
| Certora | Solidity 形式化验证 | 高 | 付费 |
| Halmos | Foundry 符号执行 | 中 | 免费 |
| Manticore | 通用符号执行 | 高 | 免费 |
| Echidna | 模糊测试 | 中 | 免费 |
6.5 测试数据管理
测试数据的质量直接影响测试效果。以下是预测市场的测试数据管理策略:
typescript
// 测试数据工厂
class TestDataFactory {
/**
* 生成测试事件
*/
static createEvent(overrides?: Partial<Event>): Event {
return {
id: uuid(),
title: `Test Event ${Date.now()}`,
description: 'A test prediction market event',
category: 'crypto',
resolutionSource: 'CoinGecko',
endTime: Math.floor(Date.now() / 1000) + 86400 * 30,
status: 'active',
yesPrice: 0.50,
noPrice: 0.50,
...overrides
};
}
/**
* 生成测试订单簿状态
*/
static createOrderBook(depth: number = 10): OrderBookSnapshot {
const bids: OrderLevel[] = [];
const asks: OrderLevel[] = [];
for (let i = 0; i < depth; i++) {
bids.push({
price: 0.50 - (i + 1) * 0.01,
quantity: Math.floor(Math.random() * 1000) + 100,
orderCount: Math.floor(Math.random() * 10) + 1
});
asks.push({
price: 0.50 + (i + 1) * 0.01,
quantity: Math.floor(Math.random() * 1000) + 100,
orderCount: Math.floor(Math.random() * 10) + 1
});
}
return { bids, asks, timestamp: Date.now() };
}
/**
* 生成压力测试用的订单流
*/
static createOrderStream(count: number, eventId: string): Order[] {
const orders: Order[] = [];
for (let i = 0; i < count; i++) {
orders.push({
id: uuid(),
eventId,
userId: `0x${randomBytes(20).toString('hex')}`,
side: Math.random() > 0.5 ? 'YES' : 'NO',
orderType: Math.random() > 0.7 ? 'MARKET' : 'LIMIT',
price: Math.round((0.3 + Math.random() * 0.4) * 100) / 100,
quantity: Math.floor(Math.random() * 500) + 10,
filledQuantity: 0,
status: 'PENDING',
timestamp: Date.now() + i
});
}
return orders;
}
}6.6 测试报告自动化
typescript
// 自动生成测试报告
class TestReportGenerator {
async generateReport(): Promise<TestReport> {
const coverage = await this.getCoverageReport();
const testResults = await this.getTestResults();
const securityScan = await this.getSecurityScanResults();
const gasReport = await this.getGasReport();
return {
timestamp: new Date().toISOString(),
summary: {
totalTests: testResults.total,
passed: testResults.passed,
failed: testResults.failed,
coverage: {
statements: coverage.statements,
branches: coverage.branches,
functions: coverage.functions,
lines: coverage.lines
},
securityIssues: securityScan.issues.length,
gasOptimizations: gasReport.optimizations.length
},
details: {
testResults,
coverage,
securityScan,
gasReport
},
recommendations: this.generateRecommendations(
coverage, testResults, securityScan, gasReport
)
};
}
private generateRecommendations(...args: any[]): string[] {
const recommendations: string[] = [];
if (args[0].statements < 90) {
recommendations.push('Statement coverage below 90%, add more unit tests');
}
if (args[2].issues.length > 0) {
recommendations.push(`Found ${args[2].issues.length} security issues, fix before deployment`);
}
return recommendations;
}
}6.7 测试最佳实践 Checklist
| 实践 | 说明 | 优先级 |
|---|---|---|
| 测试先行 | 先写测试再写代码 | P0 |
| 覆盖率 >90% | 智能合约必须高覆盖 | P0 |
| 模糊测试 | 发现边界情况 | P1 |
| 形式化验证 | 核心逻辑数学证明 | P1 |
| 混沌工程 | 验证系统韧性 | P2 |
测试数据生成 Prompt(用于 AI 生成测试用例):
text
请为预测市场订单簿合约生成测试用例,覆盖以下场景:
1. 正常下单和撮合
2. 部分成交和完全成交
3. 取消订单
4. 价格优先、时间优先
5. 边界条件(价格=0、数量=0、溢出)
6. 权限控制(非操作员下单)
7. 重入攻击防护
请使用 Hardhat + Chai 格式输出。测试环境配置最佳实践:
typescript
// 测试环境配置
const testConfig = {
// 使用独立的测试数据库
database: {
host: 'localhost',
port: 5432,
database: 'prediction_market_test',
// 每次测试前清空数据
dropBeforeTest: true
},
// 使用本地 Hardhat 网络
blockchain: {
network: 'hardhat',
mining: { auto: true, interval: 0 }
},
// Mock 外部服务
mocks: {
oracle: true,
aiService: true,
notificationService: true
}
};6.8 测试环境管理最佳实践
测试环境的管理直接影响测试效率和结果可靠性:
typescript
// 测试环境配置管理
interface TestEnvironmentConfig {
// 区块链环境
blockchain: {
network: 'hardhat' | 'localhost' | 'testnet';
blockTime: number; // 出块时间
accounts: number; // 测试账户数
initialBalance: string; // 初始余额
};
// 数据库环境
database: {
host: string;
port: number;
database: string;
dropBeforeTest: boolean; // 测试前清空
seedData: boolean; // 填充测试数据
};
// 外部服务 Mock
mocks: {
oracle: boolean; // Mock 预言机
aiService: boolean; // Mock AI 服务
notification: boolean; // Mock 通知服务
blockchain: boolean; // Mock 区块链
};
}
// 测试数据清理
class TestCleanup {
async cleanup(): Promise<void> {
// 1. 清空数据库
await this.truncateTables();
// 2. 重置区块链状态
await this.resetBlockchain();
// 3. 清理缓存
await this.flushRedis();
// 4. 清理文件
await this.cleanupFiles();
}
}测试环境管理 Checklist:
| 检查项 | 说明 | 工具 |
|---|---|---|
| 环境隔离 | 测试环境独立于生产 | Docker |
| 数据隔离 | 每次测试使用干净数据 | 数据库快照 |
| 服务 Mock | Mock 外部依赖 | Jest Mock |
| 并行执行 | 测试并行化提升效率 | Jest Workers |
| 超时设置 | 避免测试卡死 | Jest Timeout |
参考与延伸
[11] Docker Testing(2025)— Docker 测试环境
[12] TestContainers(2025)— 容器化测试
[1] Hardhat Testing(2025)— 合约测试指南
[2] GitHub Actions(2025)— CI/CD 流水线
[3] Grafana(2025)— 监控可视化
[4] k6(2025)— 压力测试工具
[5] Slither(2025)— 合约安全分析
[6] Chaos Engineering(2025)— 混沌工程原则
[7] Foundry Fuzz Testing(2025)— 模糊测试指南
[8] Certora(2025)— 合约形式化验证平台
[9] Playwright(2025)— 端到端测试框架
[10] Site Reliability Engineering(2016)— Google SRE 可靠性工程