Files
xlxumu/docs/operations/测试文档.md

30 KiB
Raw Blame History

测试文档

版本历史

版本 日期 作者 变更说明
1.0 2024-01-20 测试团队 初始版本
1.1 2024-09-21 测试团队 更新测试策略,与实际项目保持一致

1. 测试概述

1.1 测试目标

确保畜牧养殖管理平台的功能完整性、性能稳定性、安全可靠性,为用户提供高质量的软件产品。

1.2 测试范围

  • 功能测试:验证系统功能是否符合需求规格说明
  • 性能测试:验证系统在各种负载下的性能表现
  • 安全测试:验证系统的安全防护能力
  • 兼容性测试:验证系统在不同环境下的兼容性
  • 用户体验测试:验证系统的易用性和用户体验

1.3 测试策略

采用分层测试策略,包括单元测试、集成测试、系统测试和验收测试。

graph TD
    A[验收测试] --> B[系统测试]
    B --> C[集成测试]
    C --> D[单元测试]
    
    D --> D1[前端单元测试]
    D --> D2[后端单元测试]
    D --> D3[小程序单元测试]
    
    C --> C1[API集成测试]
    C --> C2[数据库集成测试]
    C --> C3[第三方服务集成测试]
    
    B --> B1[功能测试]
    B --> B2[性能测试]
    B --> B3[安全测试]
    
    A --> A1[用户验收测试]
    A --> A2[业务验收测试]

2. 测试环境

2.1 测试环境配置

环境类型 服务器配置 数据库 域名 用途
开发测试环境 2核4G MySQL 8.0 dev-test.xlxumu.com 开发阶段测试
集成测试环境 4核8G MySQL 8.0 + Redis test.xlxumu.com 集成测试
性能测试环境 8核16G MySQL 8.0 + Redis perf.xlxumu.com 性能测试
预生产环境 8核16G MySQL 8.0 + Redis pre.xlxumu.com 生产前验证

2.2 测试数据准备

-- 测试数据初始化脚本
-- 用户测试数据
INSERT INTO users (username, password, role, status) VALUES
('test_admin', 'hashed_password', 'admin', 'active'),
('test_farmer', 'hashed_password', 'farmer', 'active'),
('test_trader', 'hashed_password', 'trader', 'active');

-- 养殖场测试数据
INSERT INTO farms (name, owner_id, location, area, status) VALUES
('测试养殖场1', 1, '北京市朝阳区', 1000, 'active'),
('测试养殖场2', 2, '河北省承德市', 2000, 'active');

-- 动物测试数据
INSERT INTO animals (farm_id, breed, birth_date, gender, status) VALUES
(1, '安格斯牛', '2023-01-15', 'male', 'healthy'),
(1, '西门塔尔牛', '2023-02-20', 'female', 'healthy'),
(2, '夏洛莱牛', '2023-03-10', 'male', 'healthy');

3. 单元测试

3.1 前端单元测试

3.1.1 Vue组件测试

// tests/unit/components/AnimalCard.spec.js
import { mount } from '@vue/test-utils'
import AnimalCard from '@/components/AnimalCard.vue'

describe('AnimalCard.vue', () => {
  it('renders animal information correctly', () => {
    const animal = {
      id: 1,
      breed: '安格斯牛',
      age: 12,
      weight: 450,
      status: 'healthy'
    }
    
    const wrapper = mount(AnimalCard, {
      props: { animal }
    })
    
    expect(wrapper.text()).toContain('安格斯牛')
    expect(wrapper.text()).toContain('12')
    expect(wrapper.text()).toContain('450')
    expect(wrapper.find('.status-healthy')).toBeTruthy()
  })
  
  it('emits edit event when edit button clicked', async () => {
    const animal = { id: 1, breed: '安格斯牛' }
    const wrapper = mount(AnimalCard, {
      props: { animal }
    })
    
    await wrapper.find('.edit-btn').trigger('click')
    expect(wrapper.emitted().edit).toBeTruthy()
    expect(wrapper.emitted().edit[0]).toEqual([animal])
  })
})

3.1.2 Vuex Store测试

// tests/unit/store/animals.spec.js
import { createStore } from 'vuex'
import animalsModule from '@/store/modules/animals'

describe('animals store module', () => {
  let store
  
  beforeEach(() => {
    store = createStore({
      modules: {
        animals: animalsModule
      }
    })
  })
  
  it('should fetch animals list', async () => {
    const mockAnimals = [
      { id: 1, breed: '安格斯牛' },
      { id: 2, breed: '西门塔尔牛' }
    ]
    
    // Mock API response
    jest.spyOn(api, 'getAnimals').mockResolvedValue(mockAnimals)
    
    await store.dispatch('animals/fetchAnimals')
    
    expect(store.state.animals.list).toEqual(mockAnimals)
    expect(store.state.animals.loading).toBe(false)
  })
})

3.2 后端单元测试

3.2.1 API接口测试

// tests/unit/controllers/animals.test.js
const request = require('supertest')
const app = require('../../../app')
const { Animal } = require('../../../models')

describe('Animals API', () => {
  beforeEach(async () => {
    await Animal.destroy({ where: {} })
  })
  
  describe('GET /api/animals', () => {
    it('should return animals list', async () => {
      // 创建测试数据
      await Animal.create({
        breed: '安格斯牛',
        age: 12,
        weight: 450,
        status: 'healthy'
      })
      
      const response = await request(app)
        .get('/api/animals')
        .expect(200)
      
      expect(response.body.success).toBe(true)
      expect(response.body.data).toHaveLength(1)
      expect(response.body.data[0].breed).toBe('安格斯牛')
    })
  })
  
  describe('POST /api/animals', () => {
    it('should create new animal', async () => {
      const animalData = {
        breed: '西门塔尔牛',
        age: 10,
        weight: 400,
        status: 'healthy'
      }
      
      const response = await request(app)
        .post('/api/animals')
        .send(animalData)
        .expect(201)
      
      expect(response.body.success).toBe(true)
      expect(response.body.data.breed).toBe('西门塔尔牛')
      
      // 验证数据库中是否创建成功
      const animal = await Animal.findByPk(response.body.data.id)
      expect(animal).toBeTruthy()
      expect(animal.breed).toBe('西门塔尔牛')
    })
    
    it('should validate required fields', async () => {
      const response = await request(app)
        .post('/api/animals')
        .send({})
        .expect(400)
      
      expect(response.body.success).toBe(false)
      expect(response.body.message).toContain('breed is required')
    })
  })
})

3.2.2 业务逻辑测试

// tests/unit/services/animalService.test.js
const AnimalService = require('../../../services/animalService')
const { Animal } = require('../../../models')

describe('AnimalService', () => {
  describe('calculateAge', () => {
    it('should calculate age correctly', () => {
      const birthDate = new Date('2022-01-01')
      const age = AnimalService.calculateAge(birthDate)
      
      expect(age).toBeGreaterThan(0)
      expect(typeof age).toBe('number')
    })
  })
  
  describe('getHealthStatus', () => {
    it('should return healthy status for normal weight', () => {
      const animal = {
        breed: '安格斯牛',
        weight: 450,
        age: 12
      }
      
      const status = AnimalService.getHealthStatus(animal)
      expect(status).toBe('healthy')
    })
    
    it('should return underweight status for low weight', () => {
      const animal = {
        breed: '安格斯牛',
        weight: 200,
        age: 12
      }
      
      const status = AnimalService.getHealthStatus(animal)
      expect(status).toBe('underweight')
    })
  })
})

3.3 小程序单元测试

// miniprogram/tests/utils/formatDate.test.js
const { formatDate } = require('../../utils/formatDate')

describe('formatDate', () => {
  it('should format date correctly', () => {
    const date = new Date('2024-01-15T10:30:00')
    const formatted = formatDate(date, 'YYYY-MM-DD')
    
    expect(formatted).toBe('2024-01-15')
  })
  
  it('should handle invalid date', () => {
    const formatted = formatDate(null, 'YYYY-MM-DD')
    
    expect(formatted).toBe('')
  })
})

4. 集成测试

4.1 API集成测试

// tests/integration/api.test.js
const request = require('supertest')
const app = require('../../app')

describe('API Integration Tests', () => {
  let authToken
  
  beforeAll(async () => {
    // 登录获取token
    const loginResponse = await request(app)
      .post('/api/auth/login')
      .send({
        username: 'test_admin',
        password: 'test_password'
      })
    
    authToken = loginResponse.body.data.token
  })
  
  describe('Animals CRUD Operations', () => {
    let animalId
    
    it('should create animal', async () => {
      const response = await request(app)
        .post('/api/animals')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          breed: '安格斯牛',
          farmId: 1,
          birthDate: '2023-01-15',
          gender: 'male'
        })
        .expect(201)
      
      animalId = response.body.data.id
      expect(response.body.success).toBe(true)
    })
    
    it('should get animal details', async () => {
      const response = await request(app)
        .get(`/api/animals/${animalId}`)
        .set('Authorization', `Bearer ${authToken}`)
        .expect(200)
      
      expect(response.body.data.breed).toBe('安格斯牛')
    })
    
    it('should update animal', async () => {
      const response = await request(app)
        .put(`/api/animals/${animalId}`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          weight: 450
        })
        .expect(200)
      
      expect(response.body.data.weight).toBe(450)
    })
    
    it('should delete animal', async () => {
      await request(app)
        .delete(`/api/animals/${animalId}`)
        .set('Authorization', `Bearer ${authToken}`)
        .expect(200)
      
      // 验证删除成功
      await request(app)
        .get(`/api/animals/${animalId}`)
        .set('Authorization', `Bearer ${authToken}`)
        .expect(404)
    })
  })
})

4.2 数据库集成测试

// tests/integration/database.test.js
const { sequelize, User, Farm, Animal } = require('../../models')

describe('Database Integration Tests', () => {
  beforeAll(async () => {
    await sequelize.sync({ force: true })
  })
  
  afterAll(async () => {
    await sequelize.close()
  })
  
  describe('Model Associations', () => {
    it('should create user with farm and animals', async () => {
      // 创建用户
      const user = await User.create({
        username: 'test_farmer',
        email: 'farmer@test.com',
        password: 'hashed_password',
        role: 'farmer'
      })
      
      // 创建养殖场
      const farm = await Farm.create({
        name: '测试养殖场',
        ownerId: user.id,
        location: '北京市朝阳区',
        area: 1000
      })
      
      // 创建动物
      const animal = await Animal.create({
        farmId: farm.id,
        breed: '安格斯牛',
        birthDate: new Date('2023-01-15'),
        gender: 'male'
      })
      
      // 验证关联关系
      const userWithFarms = await User.findByPk(user.id, {
        include: [{
          model: Farm,
          include: [Animal]
        }]
      })
      
      expect(userWithFarms.Farms).toHaveLength(1)
      expect(userWithFarms.Farms[0].Animals).toHaveLength(1)
      expect(userWithFarms.Farms[0].Animals[0].breed).toBe('安格斯牛')
    })
  })
})

5. 系统测试

5.1 功能测试用例

5.1.1 用户管理功能测试

测试用例ID 测试场景 测试步骤 预期结果
TC_USER_001 用户注册 1. 访问注册页面
2. 填写用户信息
3. 点击注册按钮
注册成功,跳转到登录页面
TC_USER_002 用户登录 1. 访问登录页面
2. 输入用户名密码
3. 点击登录按钮
登录成功,跳转到首页
TC_USER_003 密码重置 1. 点击忘记密码
2. 输入邮箱
3. 查收邮件重置密码
密码重置成功

5.1.2 养殖管理功能测试

测试用例ID 测试场景 测试步骤 预期结果
TC_FARM_001 添加养殖场 1. 进入养殖场管理
2. 点击添加养殖场
3. 填写养殖场信息
4. 保存
养殖场添加成功
TC_ANIMAL_001 添加动物档案 1. 进入动物管理
2. 点击添加动物
3. 填写动物信息
4. 保存
动物档案创建成功
TC_HEALTH_001 健康记录管理 1. 选择动物
2. 添加健康记录
3. 填写检查结果
4. 保存
健康记录保存成功

5.2 自动化测试脚本

5.2.1 Selenium Web自动化测试

# tests/e2e/test_user_login.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import unittest

class TestUserLogin(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get("http://test.xlxumu.com")
    
    def tearDown(self):
        self.driver.quit()
    
    def test_successful_login(self):
        # 点击登录按钮
        login_btn = self.driver.find_element(By.ID, "login-btn")
        login_btn.click()
        
        # 输入用户名
        username_input = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.ID, "username"))
        )
        username_input.send_keys("test_admin")
        
        # 输入密码
        password_input = self.driver.find_element(By.ID, "password")
        password_input.send_keys("test_password")
        
        # 点击登录
        submit_btn = self.driver.find_element(By.ID, "submit-btn")
        submit_btn.click()
        
        # 验证登录成功
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "dashboard"))
        )
        
        self.assertIn("仪表板", self.driver.title)
    
    def test_invalid_credentials(self):
        # 输入错误的用户名密码
        login_btn = self.driver.find_element(By.ID, "login-btn")
        login_btn.click()
        
        username_input = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.ID, "username"))
        )
        username_input.send_keys("invalid_user")
        
        password_input = self.driver.find_element(By.ID, "password")
        password_input.send_keys("invalid_password")
        
        submit_btn = self.driver.find_element(By.ID, "submit-btn")
        submit_btn.click()
        
        # 验证错误提示
        error_message = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "error-message"))
        )
        
        self.assertIn("用户名或密码错误", error_message.text)

if __name__ == "__main__":
    unittest.main()

5.2.2 小程序自动化测试

// tests/e2e/miniprogram/login.test.js
const automator = require('miniprogram-automator')

describe('小程序登录测试', () => {
  let miniProgram
  let page
  
  beforeAll(async () => {
    miniProgram = await automator.launch({
      cliPath: '/Applications/wechatwebdevtools.app/Contents/MacOS/cli',
      projectPath: './miniprogram'
    })
    
    page = await miniProgram.reLaunch('/pages/login/login')
  })
  
  afterAll(async () => {
    await miniProgram.close()
  })
  
  it('should login successfully with valid credentials', async () => {
    // 输入用户名
    const usernameInput = await page.$('.username-input')
    await usernameInput.input('test_user')
    
    // 输入密码
    const passwordInput = await page.$('.password-input')
    await passwordInput.input('test_password')
    
    // 点击登录按钮
    const loginBtn = await page.$('.login-btn')
    await loginBtn.tap()
    
    // 等待跳转到首页
    await page.waitFor(2000)
    
    // 验证当前页面
    const currentPath = await page.path
    expect(currentPath).toBe('pages/index/index')
  })
})

6. 性能测试

6.1 性能测试指标

指标类型 指标名称 目标值 测试方法
响应时间 页面加载时间 < 3秒 浏览器性能测试
响应时间 API响应时间 < 500ms 接口压力测试
吞吐量 并发用户数 1000+ 负载测试
吞吐量 每秒请求数 500+ 压力测试
资源使用 CPU使用率 < 70% 系统监控
资源使用 内存使用率 < 80% 系统监控

6.2 JMeter性能测试脚本

<!-- performance-test.jmx -->
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="畜牧管理平台性能测试">
      <elementProp name="TestPlan.arguments" elementType="Arguments" guiclass="ArgumentsPanel">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
    </TestPlan>
    
    <hashTree>
      <!-- 线程组配置 -->
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="用户登录压力测试">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">10</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">100</stringProp>
        <stringProp name="ThreadGroup.ramp_time">60</stringProp>
      </ThreadGroup>
      
      <hashTree>
        <!-- HTTP请求配置 -->
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="登录请求">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments">
              <elementProp name="" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">{"username":"test_user","password":"test_password"}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">test.xlxumu.com</stringProp>
          <stringProp name="HTTPSampler.port">80</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.path">/api/auth/login</stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
        </HTTPSamplerProxy>
        
        <!-- 响应断言 -->
        <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言">
          <collectionProp name="Asserion.test_strings">
            <stringProp name="success">true</stringProp>
          </collectionProp>
          <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
          <boolProp name="Assertion.assume_success">false</boolProp>
          <intProp name="Assertion.test_type">2</intProp>
        </ResponseAssertion>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

6.3 性能监控脚本

#!/bin/bash
# performance-monitor.sh

# 性能监控脚本
echo "开始性能监控..."

# 监控CPU使用率
echo "CPU使用率:"
top -l 1 | grep "CPU usage"

# 监控内存使用率
echo "内存使用率:"
vm_stat | grep "Pages free\|Pages active\|Pages inactive\|Pages speculative\|Pages wired down"

# 监控磁盘使用率
echo "磁盘使用率:"
df -h

# 监控网络连接
echo "网络连接数:"
netstat -an | grep ESTABLISHED | wc -l

# 监控数据库连接
echo "数据库连接数:"
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"

# 监控Redis连接
echo "Redis连接数:"
redis-cli info clients | grep connected_clients

7. 安全测试

7.1 安全测试检查项

安全类型 检查项 测试方法 风险等级
身份认证 弱密码检测 密码策略测试
身份认证 会话管理 会话超时测试
授权控制 权限绕过 越权访问测试
数据验证 SQL注入 注入攻击测试
数据验证 XSS攻击 脚本注入测试
数据传输 HTTPS加密 传输加密测试
数据存储 敏感数据加密 数据加密测试

7.2 安全测试工具

7.2.1 OWASP ZAP自动化扫描

# security_scan.py
from zapv2 import ZAPv2
import time

# ZAP代理配置
zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'})

# 目标URL
target = 'http://test.xlxumu.com'

# 开始爬虫扫描
print('开始爬虫扫描...')
scanid = zap.spider.scan(target)
time.sleep(2)

while int(zap.spider.status(scanid)) < 100:
    print(f'爬虫进度: {zap.spider.status(scanid)}%')
    time.sleep(2)

print('爬虫扫描完成')

# 开始主动扫描
print('开始主动安全扫描...')
scanid = zap.ascan.scan(target)
while int(zap.ascan.status(scanid)) < 100:
    print(f'扫描进度: {zap.ascan.status(scanid)}%')
    time.sleep(5)

print('安全扫描完成')

# 生成报告
print('生成安全报告...')
with open('security_report.html', 'w') as f:
    f.write(zap.core.htmlreport())

print('安全报告已生成: security_report.html')

7.2.2 SQL注入测试

# sql_injection_test.py
import requests
import json

def test_sql_injection():
    """SQL注入测试"""
    base_url = "http://test.xlxumu.com/api"
    
    # 测试用例
    injection_payloads = [
        "' OR '1'='1",
        "'; DROP TABLE users; --",
        "' UNION SELECT * FROM users --",
        "1' AND (SELECT COUNT(*) FROM users) > 0 --"
    ]
    
    # 测试登录接口
    for payload in injection_payloads:
        data = {
            "username": payload,
            "password": "test"
        }
        
        response = requests.post(f"{base_url}/auth/login", json=data)
        
        if response.status_code == 200:
            result = response.json()
            if result.get('success'):
                print(f"⚠️  可能存在SQL注入漏洞: {payload}")
            else:
                print(f"✅ 防护正常: {payload}")
        else:
            print(f"✅ 请求被拒绝: {payload}")

if __name__ == "__main__":
    test_sql_injection()

8. 测试报告

8.1 测试执行报告模板

# 测试执行报告

## 基本信息
- **项目名称**: 畜牧养殖管理平台
- **测试版本**: v1.0.0
- **测试环境**: 测试环境
- **测试时间**: 2024-01-20 ~ 2024-01-25
- **测试负责人**: 张三

## 测试概况
- **计划测试用例**: 150个
- **实际执行用例**: 148个
- **通过用例**: 142个
- **失败用例**: 6个
- **阻塞用例**: 2个
- **测试通过率**: 95.9%

## 功能测试结果

| 功能模块 | 计划用例 | 执行用例 | 通过用例 | 失败用例 | 通过率 |
|----------|----------|----------|----------|----------|--------|
| 用户管理 | 25 | 25 | 24 | 1 | 96% |
| 养殖管理 | 40 | 40 | 38 | 2 | 95% |
| 交易管理 | 30 | 30 | 29 | 1 | 97% |
| 财务管理 | 20 | 20 | 19 | 1 | 95% |
| 系统管理 | 15 | 15 | 15 | 0 | 100% |
| 数据统计 | 20 | 18 | 17 | 1 | 94% |

## 性能测试结果

| 测试指标 | 目标值 | 实际值 | 是否达标 |
|----------|--------|--------|----------|
| 页面加载时间 | < 3秒 | 2.1秒 |  |
| API响应时间 | < 500ms | 320ms |  |
| 并发用户数 | 1000+ | 1200 |  |
| CPU使用率 | < 70% | 65% |  |
| 内存使用率 | < 80% | 72% |  |

## 安全测试结果

| 安全项目 | 测试结果 | 风险等级 | 处理状态 |
|----------|----------|----------|----------|
| SQL注入 | 无漏洞 |  |  |
| XSS攻击 | 发现1个 |  | 🔄 修复中 |
| 权限控制 | 无问题 |  |  |
| 数据加密 | 符合要求 |  |  |

## 缺陷统计

| 缺陷等级 | 数量 | 已修复 | 待修复 |
|----------|------|--------|--------|
| 严重 | 2 | 1 | 1 |
| 一般 | 4 | 3 | 1 |
| 轻微 | 8 | 6 | 2 |
| 建议 | 5 | 2 | 3 |

## 测试结论
系统整体功能完整性能表现良好安全防护到位建议修复剩余缺陷后发布

8.2 自动化测试报告生成

// generate-test-report.js
const fs = require('fs')
const path = require('path')

class TestReporter {
  constructor() {
    this.results = {
      total: 0,
      passed: 0,
      failed: 0,
      skipped: 0,
      suites: []
    }
  }
  
  addSuite(suite) {
    this.results.suites.push(suite)
    this.results.total += suite.tests.length
    this.results.passed += suite.tests.filter(t => t.status === 'passed').length
    this.results.failed += suite.tests.filter(t => t.status === 'failed').length
    this.results.skipped += suite.tests.filter(t => t.status === 'skipped').length
  }
  
  generateHTML() {
    const template = `
    <!DOCTYPE html>
    <html>
    <head>
      <title>测试报告</title>
      <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .summary { background: #f5f5f5; padding: 15px; border-radius: 5px; }
        .passed { color: green; }
        .failed { color: red; }
        .skipped { color: orange; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
      </style>
    </head>
    <body>
      <h1>测试执行报告</h1>
      
      <div class="summary">
        <h2>测试概况</h2>
        <p>总用例数: ${this.results.total}</p>
        <p class="passed">通过: ${this.results.passed}</p>
        <p class="failed">失败: ${this.results.failed}</p>
        <p class="skipped">跳过: ${this.results.skipped}</p>
        <p>通过率: ${((this.results.passed / this.results.total) * 100).toFixed(2)}%</p>
      </div>
      
      <h2>详细结果</h2>
      <table>
        <thead>
          <tr>
            <th>测试套件</th>
            <th>测试用例</th>
            <th>状态</th>
            <th>执行时间</th>
            <th>错误信息</th>
          </tr>
        </thead>
        <tbody>
          ${this.results.suites.map(suite => 
            suite.tests.map(test => `
              <tr>
                <td>${suite.name}</td>
                <td>${test.name}</td>
                <td class="${test.status}">${test.status}</td>
                <td>${test.duration}ms</td>
                <td>${test.error || ''}</td>
              </tr>
            `).join('')
          ).join('')}
        </tbody>
      </table>
      
      <p>报告生成时间: ${new Date().toLocaleString()}</p>
    </body>
    </html>
    `
    
    return template
  }
  
  saveReport(filename = 'test-report.html') {
    const html = this.generateHTML()
    fs.writeFileSync(filename, html)
    console.log(`测试报告已生成: ${filename}`)
  }
}

module.exports = TestReporter

9. 测试工具和环境

9.1 测试工具清单

工具类型 工具名称 版本 用途
单元测试 Jest 29.x JavaScript单元测试
单元测试 Vue Test Utils 2.x Vue组件测试
集成测试 Supertest 6.x API集成测试
E2E测试 Selenium 4.x Web自动化测试
E2E测试 Miniprogram Automator 0.x 小程序自动化测试
性能测试 JMeter 5.x 性能压力测试
安全测试 OWASP ZAP 2.x 安全漏洞扫描
测试管理 TestRail - 测试用例管理

9.2 CI/CD集成

# .github/workflows/test.yml
name: 自动化测试

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: test_db
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
      
      redis:
        image: redis:6-alpine
        options: >-
          --health-cmd="redis-cli ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        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
      env:
        DATABASE_URL: mysql://root:root@localhost:3306/test_db
        REDIS_URL: redis://localhost:6379
    
    - name: Run E2E tests
      run: npm run test:e2e
    
    - name: Generate test report
      run: npm run test:report
    
    - name: Upload test results
      uses: actions/upload-artifact@v3
      with:
        name: test-results
        path: test-results/

10. 总结

10.1 测试策略总结

本测试文档建立了完整的测试体系,包括:

  1. 多层次测试:从单元测试到系统测试的完整覆盖
  2. 自动化测试:提高测试效率和准确性
  3. 性能测试:确保系统性能满足要求
  4. 安全测试:保障系统安全可靠
  5. 持续集成:自动化测试流程

10.2 测试质量保证

  • 测试覆盖率:代码覆盖率 > 80%
  • 自动化率:自动化测试覆盖率 > 70%
  • 缺陷密度< 2个缺陷/KLOC
  • 测试效率:回归测试时间 < 2小时

10.3 持续改进

  1. 测试工具升级:定期更新测试工具和框架
  2. 测试流程优化:持续优化测试流程和方法
  3. 团队技能提升:加强测试团队技能培训
  4. 质量度量:建立完善的质量度量体系

文档版本: v1.0.0
最后更新: 2024年12月
维护团队: 测试团队