WeChat Bot自动化测试:CI/CD流程配置

WeChat Bot自动化测试:CI/CD流程配置

【免费下载链接】wechat-bot 🤖一个基于 WeChaty 结合 DeepSeek / ChatGPT / Kimi / 讯飞等Ai服务实现的微信机器人 ,可以用来帮助你自动回复微信消息,或者管理微信群/好友,检测僵尸粉等... 【免费下载链接】wechat-bot 项目地址: https://gitcode.com/GitHub_Trending/we/wechat-bot

引言:为什么自动化测试与CI/CD对微信机器人至关重要

在微信机器人开发中,自动化测试和CI/CD流程是保障项目质量和效率的关键环节。随着业务复杂度提升,手动测试难以覆盖所有场景,而CI/CD能够实现代码提交即测试、测试通过即部署的自动化流程。本文将以WeChat Bot项目为例,详细介绍如何构建完整的自动化测试与CI/CD体系。

项目测试现状分析

现有测试脚本解析

WeChat Bot项目已具备基础测试能力,在package.json中定义了多个测试脚本:

{
  "scripts": {
    "test": "node ./src/wechaty/testMessage.js",
    "test-openai": "node ./src/openai/__test__.js",
    "test-xunfei": "node ./src/xunfei/__test__.js",
    "test-kimi": "node ./src/kimi/__test__.js",
    "test-dify": "node ./src/dify/__test__.js"
  }
}

这些脚本针对不同AI服务提供商(OpenAI、讯飞、Kimi等)实现了基础测试,例如DeepSeek的测试脚本src/deepseek-free/__test__.js

import { getDeepSeekFreeReply } from './index.js'

async function testMessage() {
  const message = await getDeepSeekFreeReply('hello')
  console.log('🌸🌸🌸 / message: ', message)
}

testMessage()

当前测试体系的局限性

  1. 缺乏自动化触发机制:测试需手动执行,无法在代码变更时自动触发
  2. 无环境隔离:开发环境与测试环境未分离,易受本地配置影响
  3. 测试覆盖率不足:仅覆盖API调用,缺乏端到端测试和集成测试
  4. 部署流程手动化:Docker部署需手动构建和推送镜像

CI/CD流程设计与实现

CI/CD架构设计

mermaid

环境准备与依赖配置

1. 必要依赖安装

确保项目中已安装测试和CI相关依赖:

# 安装测试工具
npm install --save-dev jest supertest
# 安装代码质量工具
npm install --save-dev eslint prettier husky lint-staged
2. 测试环境变量配置

创建CI专用环境变量文件.env.ci

# CI环境专用配置
LOG_LEVEL=error
TEST_MODE=true
# AI服务测试密钥(使用CI/CD平台的secret存储)
DEEPSEEK_FREE_TOKEN=${{ secrets.DEEPSEEK_FREE_TOKEN }}
OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}

GitHub Actions工作流配置

在项目中创建.github/workflows/ci-cd.yml文件:

name: WeChat Bot CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  quality-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Code formatting check
        run: npm run format -- --check
      - name: Lint code
        run: eslint . --ext .js,.mjs

  unit-test:
    needs: quality-check
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: [openai, xunfei, kimi, dify, deepseek-free]
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Create .env file
        run: |
          echo "DEEPSEEK_FREE_TOKEN=${{ secrets.DEEPSEEK_FREE_TOKEN }}" > .env
          echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .env
          echo "XUNFEI_APP_ID=${{ secrets.XUNFEI_APP_ID }}" >> .env
          echo "XUNFEI_API_KEY=${{ secrets.XUNFEI_API_KEY }}" >> .env
          echo "XUNFEI_API_SECRET=${{ secrets.XUNFEI_API_SECRET }}" >> .env
          echo "KIMI_API_KEY=${{ secrets.KIMI_API_KEY }}" >> .env
          echo "DIFY_API_KEY=${{ secrets.DIFY_API_KEY }}" >> .env
      - name: Run ${{ matrix.service }} tests
        run: npm run test-${{ matrix.service }}

  integration-test:
    needs: unit-test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Run integration tests
        run: npm run test

  build-and-deploy:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: [unit-test, integration-test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: username/wechat-bot:latest,username/wechat-bot:${{ github.sha }}
      
      - name: Deploy to production
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/wechat-bot
            docker-compose pull
            docker-compose up -d

测试策略与实现

单元测试增强

以DeepSeek服务为例,改进测试脚本src/deepseek-free/__test__.js

import { getDeepSeekFreeReply } from './index.js';
import axios from 'axios';

// Mock axios
jest.mock('axios');

describe('DeepSeek Free API Tests', () => {
  beforeEach(() => {
    process.env.DEEPSEEK_FREE_TOKEN = 'test-token';
    process.env.DEEPSEEK_FREE_URL = 'https://api.deepseek.com/chat/completions';
    process.env.DEEPSEEK_FREE_MODEL = 'deepseek-chat';
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  test('should return valid response for simple query', async () => {
    // Mock successful response
    axios.post.mockResolvedValue({
      data: {
        choices: [{
          message: {
            content: 'Hello from DeepSeek!'
          }
        }]
      }
    });

    const response = await getDeepSeekFreeReply('Hello');
    
    expect(response).toBe('Hello from DeepSeek!');
    expect(axios.post).toHaveBeenCalledWith(
      'https://api.deepseek.com/chat/completions',
      expect.objectContaining({
        model: 'deepseek-chat',
        messages: expect.any(Array)
      }),
      expect.objectContaining({
        headers: expect.objectContaining({
          'Authorization': 'Bearer test-token'
        })
      })
    );
  });

  test('should handle API errors gracefully', async () => {
    // Mock error response
    axios.post.mockRejectedValue(new Error('API request failed'));

    const response = await getDeepSeekFreeReply('Hello');
    
    expect(response).toContain('Error');
    expect(response).toContain('API request failed');
  });
});

集成测试实现

创建tests/integration/wechaty.test.js文件:

import { WechatyBuilder } from 'wechaty';
import { PuppetMock } from 'wechaty-puppet-mock';
import { onMessage } from '../../src/wechaty/onMessage.js';

describe('WeChat Bot Integration Tests', () => {
  let bot;

  beforeAll(async () => {
    bot = WechatyBuilder.build({
      name: 'test-bot',
      puppet: new PuppetMock(),
    });
    await bot.start();
  });

  afterAll(async () => {
    await bot.stop();
  });

  test('should auto-reply to whitelisted contact', async () => {
    // Create mock contact and message
    const contact = bot.Contact.load('test-contact');
    await contact.ready();
    
    // Mock contact is in whitelist
    process.env.ALIAS_WHITELIST = 'test-contact';
    
    // Mock message
    const message = bot.Message.load('test-message-id');
    message.say = jest.fn();
    message.talker().mockResolvedValue(contact);
    message.text().mockReturnValue('Hello bot');
    
    // Trigger message handler
    await onMessage(message);
    
    // Verify reply
    expect(message.say).toHaveBeenCalled();
  });

  test('should not reply to non-whitelisted contact', async () => {
    // Create mock contact and message
    const contact = bot.Contact.load('stranger');
    await contact.ready();
    
    // Mock contact not in whitelist
    process.env.ALIAS_WHITELIST = 'test-contact';
    
    // Mock message
    const message = bot.Message.load('test-message-id-2');
    message.say = jest.fn();
    message.talker().mockResolvedValue(contact);
    message.text().mockReturnValue('Hello bot');
    
    // Trigger message handler
    await onMessage(message);
    
    // Verify no reply
    expect(message.say).not.toHaveBeenCalled();
  });
});

端到端测试方案

使用Puppeteer模拟微信网页版登录流程,创建tests/e2e/login.test.js

import puppeteer from 'puppeteer';

describe('WeChat Bot E2E Tests', () => {
  let browser;
  let page;

  beforeAll(async () => {
    browser = await puppeteer.launch({
      headless: 'new',
      args: ['--no-sandbox', '--disable-setuid-sandbox']
    });
    page = await browser.newPage();
  });

  afterAll(async () => {
    await browser.close();
  });

  test('should start bot and show QR code', async () => {
    // Start bot in test mode
    const botProcess = require('child_process').spawn('npm', ['run', 'dev'], {
      env: { ...process.env, TEST_MODE: 'true' }
    });

    // Wait for QR code output
    let qrCodeOutput = '';
    for await (const data of botProcess.stdout) {
      qrCodeOutput += data.toString();
      if (qrCodeOutput.includes('QR Code')) break;
    }

    // Verify QR code is generated
    expect(qrCodeOutput).toContain('QR Code');
    expect(qrCodeOutput).toContain('Scan with WeChat');

    // Stop bot
    botProcess.kill();
  }, 30000);
});

Docker容器化与部署优化

Dockerfile优化

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

# Test stage
FROM builder AS tester
RUN npm ci
RUN npm test

# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/src ./src
COPY --from=builder /app/cli.js ./

# Health check
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["npm", "run", "start"]

Docker Compose配置

创建docker-compose.yml用于本地开发和测试:

version: '3.8'

services:
  wechat-bot:
    build:
      context: .
      target: builder
    volumes:
      - .:/app
      - /app/node_modules
    environment_file:
      - .env
    command: npm run dev
    ports:
      - "3000:3000"
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 3s
      retries: 3

测试报告与质量监控

测试覆盖率报告

package.json中添加覆盖率报告脚本:

{
  "scripts": {
    "test:coverage": "jest --coverage",
    "test:report": "jest --coverage && open coverage/lcov-report/index.html"
  }
}

集成代码质量平台

在CI流程中添加SonarCloud代码质量检查:

# 在.github/workflows/ci-cd.yml中添加
- name: SonarCloud Scan
  uses: SonarSource/sonarcloud-github-action@master
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
  with:
    projectBaseDir: .

持续监控与告警

健康检查接口实现

创建src/health.js

import http from 'http';

export function startHealthServer() {
  const server = http.createServer((req, res) => {
    if (req.url === '/health') {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({
        status: 'ok',
        timestamp: new Date().toISOString(),
        services: {
          wechaty: global.bot?.isLoggedIn ? 'connected' : 'disconnected',
          deepseek: global.deepseekStatus || 'unknown'
        }
      }));
    } else {
      res.writeHead(404);
      res.end();
    }
  });

  server.listen(3000, () => {
    console.log('Health check server running on port 3000');
  });

  return server;
}

错误监控与告警

集成Sentry进行错误跟踪:

// src/utils/errorHandler.js
import * as Sentry from '@sentry/node';

export function initErrorMonitoring() {
  if (process.env.SENTRY_DSN) {
    Sentry.init({
      dsn: process.env.SENTRY_DSN,
      environment: process.env.NODE_ENV || 'development',
      tracesSampleRate: 1.0,
    });
    console.log('Sentry error monitoring initialized');
  }
}

export function captureError(error) {
  if (process.env.SENTRY_DSN) {
    Sentry.captureException(error);
  }
  console.error('Error:', error);
}

总结与最佳实践

CI/CD流程优化建议

  1. 并行测试执行:在CI配置中使用矩阵策略并行运行不同服务的测试
  2. 缓存依赖:配置npm和Docker缓存减少构建时间
  3. 增量测试:只运行变更文件相关的测试用例
  4. 部署策略:采用蓝绿部署或金丝雀发布降低风险
  5. 安全扫描:集成依赖漏洞扫描工具如Snyk

自动化测试最佳实践

mermaid

下一步计划

  1. 测试覆盖率提升:目标达到80%以上代码覆盖率
  2. 性能测试:添加负载测试和性能基准测试
  3. 混沌工程:引入故障注入测试提高系统韧性
  4. 多环境部署:完善开发、测试、预发、生产环境的CI/CD流程
  5. 自动化文档:通过测试生成API文档和使用示例

通过实施本文所述的CI/CD流程和自动化测试策略,WeChat Bot项目将实现代码提交即测试、测试通过即部署的全自动化流程,大幅提升开发效率和系统稳定性,同时降低人工测试成本和人为错误风险。

【免费下载链接】wechat-bot 🤖一个基于 WeChaty 结合 DeepSeek / ChatGPT / Kimi / 讯飞等Ai服务实现的微信机器人 ,可以用来帮助你自动回复微信消息,或者管理微信群/好友,检测僵尸粉等... 【免费下载链接】wechat-bot 项目地址: https://gitcode.com/GitHub_Trending/we/wechat-bot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值