Skip to content

4.11 测试运维

一句话总结:测试保证质量,运维保证稳定——上线前的最后一道防线。

📊 学习进度

  • 状态:⬜ 未开始
  • 预计时长:4-5 小时
  • 已完成:0/3 个模块
  • 在整体流程中的位置:预测市场实战·第 8 阶段

📍 本章定位

  • 服务方案:方案 3(核心 90%)
  • 学习方式:🔥 推荐
  • 在流程中的作用:测试、部署、监控
  • 核心知识点:自动化测试、CI/CD、监控告警
  • 预计时长:4-5 小时
  • 完成后能做什么:能完成测试和部署

人机分工

环节谁做重要度说明
测试策略🧑 人⭐⭐⭐⭐⭐决定测试什么
测试用例生成🤖 AI⭐⭐⭐⭐AI 生成测试
测试执行🤖 AI⭐⭐⭐⭐自动化执行
部署策略🧑 人⭐⭐⭐⭐⭐决定部署方案
监控配置🤖 AI⭐⭐⭐AI 配置

1. 测试策略

1.1 测试金字塔

测试类型覆盖范围工具成本占比
单元测试单个函数/合约Hardhat/Foundry70%
集成测试模块间交互自定义脚本20%
端到端测试完整用户流程Playwright10%
压力测试高并发场景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 压力测试方案

测试场景并发用户持续时间目标
正常负载10010 分钟系统稳定
高峰负载5005 分钟无降级
极限负载10002 分钟优雅降级
持续压力20030 分钟无内存泄漏

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";
}
验证工具适用场景学习曲线费用
CertoraSolidity 形式化验证付费
HalmosFoundry 符号执行免费
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
数据隔离每次测试使用干净数据数据库快照
服务 MockMock 外部依赖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 可靠性工程

OPC 超级个体实战指南