Vitest Testing
提供 Vitest 单元测试与集成测试的模式与最佳实践,涵盖断言、异步测试与模拟方法。
下载 36
提供多框架测试模板与CI/CD集成,支持单元、集成、E2E及安全测试。
openclaw skills install @534422530/testing-plus命令、参数、文件名以原文为准
通过框架模板、CI/CD 集成和高级测试策略,增强测试能力。
| 测试类型 | 工具 | 目的 |
|---|---|---|
| 单元测试 | Jest、Vitest、pytest | 单个函数验证 |
| 集成测试 | Supertest、Testcontainers | 模块边界验证 |
| 端到端测试 | Playwright、Cypress | 完整用户流程验证 |
| 变异测试 | Stryker、mutmut | 测试质量验证 |
| 性能测试 | k6、Artillery | 负载测试 |
| 安全测试 | OWASP ZAP、Snyk | 漏洞检测 |
| 层级 | 比例 | 速度 | 成本 | 信心 |
|---|---|---|---|---|
| 单元测试 | ~70% | 毫秒级 | 低 | 低 |
| 集成测试 | ~20% | 秒级 | 中等 | 中等 |
| 端到端测试 | ~10% | 分钟级 | 高 | 高 |
// jest.config.js
export default {
preset: 'ts-jest',
testEnvironment: 'node',
coverageDirectory: 'coverage',
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};// src/utils.test.ts
import { calculateTotal } from './utils';
describe('calculateTotal', () => {
it('should calculate total with tax', () => {
const items = [{ price: 10, qty: 2 }, { price: 5, qty: 1 }];
const taxRate = 0.08;
expect(calculateTotal(items, taxRate)).toBe(27.0);
});
it('should handle empty items', () => {
expect(calculateTotal([], 0.08)).toBe(0);
});
});// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
},
},
});// src/utils.test.ts
import { describe, it, expect } from 'vitest';
import { calculateTotal } from './utils';
describe('calculateTotal', () => {
it('should calculate total with tax', () => {
const items = [{ price: 10, qty: 2 }, { price: 5, qty: 1 }];
expect(calculateTotal(items, 0.08)).toBe(27.0);
});
});# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short --cov=src --cov-report=term-missing# tests/test_utils.py
import pytest
from src.utils import calculate_total
def test_calculate_total_with_tax():
items = [{"price": 10, "qty": 2}, {"price": 5, "qty": 1}]
assert calculate_total(items, 0.08) == 27.0
def test_calculate_total_empty():
assert calculate_total([], 0.08) == 0
@pytest.mark.parametrize("items,tax_rate,expected", [
([{"price": 10, "qty": 1}], 0.1, 11.0),
([{"price": 20, "qty": 2}], 0.05, 42.0),
])
def test_calculate_total_parametrized(items, tax_rate, expected):
assert calculate_total(items, tax_rate) == expected// utils_test.go
package utils
import "testing"
func TestCalculateTotal(t *testing.T) {
tests := []struct {
name string
items []Item
taxRate float64
expected float64
}{
{
name: "with tax",
items: []Item{{Price: 10, Qty: 2}, {Price: 5, Qty: 1}},
taxRate: 0.08,
expected: 27.0,
},
{
name: "empty items",
items: []Item{},
taxRate: 0.08,
expected: 0,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := CalculateTotal(tc.items, tc.taxRate)
if got != tc.expected {
t.Errorf("CalculateTotal() = %f, want %f", got, tc.expected)
}
})
}
}# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info# .gitlab-ci.yml
test:
image: node:20
stage: test
script:
- npm ci
- npm test
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml# 安装
npm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner
# 运行
npx stryker run// stryker.conf.json
{
"$schema": "https://raw.githubusercontent.com/stryker-mutator/stryker/master/packages/core/schema/stryker-core.json",
"packageManager": "npm",
"reporters": ["html", "clear-text", "progress"],
"testRunner": "jest",
"jestProjectFile": "jest.config.ts",
"mutate": ["src/**/*.ts", "!src/**/*.test.ts"],
"thresholds": {
"high": 80,
"low": 60,
"break": 50
}
}# 安装
pip install mutmut
# 运行
mutmut run
# 查看结果
mutmut results
mutmut html// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 },
{ duration: '1m', target: 20 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get('https://api.example.com/users');
check(res, {
'状态码为 200': (r) => r.status === 200,
'响应时间小于 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}# load-test.yml
config:
target: "https://api.example.com"
phases:
- duration: 60
arrivalRate: 10
- duration: 120
arrivalRate: 50
- duration: 60
arrivalRate: 10
scenarios:
- name: "获取用户列表"
flow:
- get:
url: "/users"
expect:
- statusCode: 200
- contentType: json# Docker 扫描
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://example.com \
-r report.html# npm audit
npm audit
npm audit fix
# Snyk
npx snyk test
npx snyk monitor| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 测试实现细节 | 重构时测试失败 | 测试行为而非实现 |
| 不稳定测试 | 非确定性失败 | 移除时间或顺序依赖 |
| 过度模拟 | 测试内容不真实 | 仅在边界处模拟 |
| 缺少缺陷测试 | 回归问题重现 | 添加回归测试 |
已收录 7 个 Skill