1049 lines
30 KiB
Markdown
1049 lines
30 KiB
Markdown
|
|
# 测试文档
|
|||
|
|
|
|||
|
|
## 版本历史
|
|||
|
|
| 版本 | 日期 | 作者 | 变更说明 |
|
|||
|
|
|------|------|------|----------|
|
|||
|
|
| 1.0 | 2024-01-20 | 测试团队 | 初始版本 |
|
|||
|
|
| 1.1 | 2024-09-21 | 测试团队 | 更新测试策略,与实际项目保持一致 |
|
|||
|
|
|
|||
|
|
## 1. 测试概述
|
|||
|
|
|
|||
|
|
### 1.1 测试目标
|
|||
|
|
确保畜牧养殖管理平台的功能完整性、性能稳定性、安全可靠性,为用户提供高质量的软件产品。
|
|||
|
|
|
|||
|
|
### 1.2 测试范围
|
|||
|
|
- **功能测试**:验证系统功能是否符合需求规格说明
|
|||
|
|
- **性能测试**:验证系统在各种负载下的性能表现
|
|||
|
|
- **安全测试**:验证系统的安全防护能力
|
|||
|
|
- **兼容性测试**:验证系统在不同环境下的兼容性
|
|||
|
|
- **用户体验测试**:验证系统的易用性和用户体验
|
|||
|
|
|
|||
|
|
### 1.3 测试策略
|
|||
|
|
采用分层测试策略,包括单元测试、集成测试、系统测试和验收测试。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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 测试数据准备
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 测试数据初始化脚本
|
|||
|
|
-- 用户测试数据
|
|||
|
|
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组件测试
|
|||
|
|
```javascript
|
|||
|
|
// 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测试
|
|||
|
|
```javascript
|
|||
|
|
// 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接口测试
|
|||
|
|
```javascript
|
|||
|
|
// 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 业务逻辑测试
|
|||
|
|
```javascript
|
|||
|
|
// 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 小程序单元测试
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 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集成测试
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 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 数据库集成测试
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 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. 访问注册页面<br>2. 填写用户信息<br>3. 点击注册按钮 | 注册成功,跳转到登录页面 |
|
|||
|
|
| TC_USER_002 | 用户登录 | 1. 访问登录页面<br>2. 输入用户名密码<br>3. 点击登录按钮 | 登录成功,跳转到首页 |
|
|||
|
|
| TC_USER_003 | 密码重置 | 1. 点击忘记密码<br>2. 输入邮箱<br>3. 查收邮件重置密码 | 密码重置成功 |
|
|||
|
|
|
|||
|
|
#### 5.1.2 养殖管理功能测试
|
|||
|
|
|
|||
|
|
| 测试用例ID | 测试场景 | 测试步骤 | 预期结果 |
|
|||
|
|
|------------|----------|----------|----------|
|
|||
|
|
| TC_FARM_001 | 添加养殖场 | 1. 进入养殖场管理<br>2. 点击添加养殖场<br>3. 填写养殖场信息<br>4. 保存 | 养殖场添加成功 |
|
|||
|
|
| TC_ANIMAL_001 | 添加动物档案 | 1. 进入动物管理<br>2. 点击添加动物<br>3. 填写动物信息<br>4. 保存 | 动物档案创建成功 |
|
|||
|
|
| TC_HEALTH_001 | 健康记录管理 | 1. 选择动物<br>2. 添加健康记录<br>3. 填写检查结果<br>4. 保存 | 健康记录保存成功 |
|
|||
|
|
|
|||
|
|
### 5.2 自动化测试脚本
|
|||
|
|
|
|||
|
|
#### 5.2.1 Selenium Web自动化测试
|
|||
|
|
```python
|
|||
|
|
# 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 小程序自动化测试
|
|||
|
|
```javascript
|
|||
|
|
// 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性能测试脚本
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<!-- 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 性能监控脚本
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
#!/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自动化扫描
|
|||
|
|
```python
|
|||
|
|
# 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注入测试
|
|||
|
|
```python
|
|||
|
|
# 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 测试执行报告模板
|
|||
|
|
|
|||
|
|
```markdown
|
|||
|
|
# 测试执行报告
|
|||
|
|
|
|||
|
|
## 基本信息
|
|||
|
|
- **项目名称**: 畜牧养殖管理平台
|
|||
|
|
- **测试版本**: 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 自动化测试报告生成
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 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集成
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# .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月
|
|||
|
|
**维护团队**: 测试团队
|