Testing Plus

提供多框架测试模板与CI/CD集成,支持单元、集成、E2E及安全测试。

已扫描
适合谁
前端/后端开发工程师、持续集成工程师
不适合谁
非技术背景用户、无编程基础的初学者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @534422530/testing-plus

Skill 说明

命令、参数、文件名以原文为准

Testing Plus

通过框架模板、CI/CD 集成和高级测试策略,增强测试能力。

特性

  • 框架模板:支持 Jest、Vitest、pytest、Go 测试的即用型模板
  • CI/CD 集成:支持 GitHub Actions、GitLab CI、Jenkins 管道
  • 变异测试:使用 Stryker、mutmut 验证测试质量
  • 性能测试:负载测试、压力测试、基准测试
  • 安全测试:OWASP 标准、依赖项扫描、SAST(静态应用安全测试)

快速参考

测试类型工具目的
单元测试Jest、Vitest、pytest单个函数验证
集成测试Supertest、Testcontainers模块边界验证
端到端测试Playwright、Cypress完整用户流程验证
变异测试Stryker、mutmut测试质量验证
性能测试k6、Artillery负载测试
安全测试OWASP ZAP、Snyk漏洞检测

测试金字塔

层级比例速度成本信心
单元测试~70%毫秒级
集成测试~20%秒级中等中等
端到端测试~10%分钟级

框架模板

Jest(JavaScript/TypeScript)

// 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(JavaScript/TypeScript)

// 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(Python)

# 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

Go 测试

// 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)
            }
        })
    }
}

CI/CD 集成

GitHub Actions

# .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

# .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

变异测试

Stryker(JavaScript/TypeScript)

# 安装
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
  }
}

mutmut(Python)

# 安装
pip install mutmut

# 运行
mutmut run

# 查看结果
mutmut results
mutmut html

性能测试

k6(负载测试)

// 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);
}

Artillery

# 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

安全测试

OWASP ZAP

# 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

测试质量检查清单

  • [ ] 确定性 - 相同输入产生相同结果
  • [ ] 隔离性 - 无共享可变状态
  • [ ] 快速 - 单元测试:<10ms,集成测试:<1s,端到端测试:<30s
  • [ ] 可读性 - 测试名称描述测试场景
  • [ ] 可维护性 - 修改一个行为,仅需修改一个测试
  • [ ] 聚焦性 - 每个测试只包含一个逻辑断言

反模式

反模式问题解决方案
测试实现细节重构时测试失败测试行为而非实现
不稳定测试非确定性失败移除时间或顺序依赖
过度模拟测试内容不真实仅在边界处模拟
缺少缺陷测试回归问题重现添加回归测试

最佳实践

  1. 测试行为 - 而非实现细节
  2. 使用参数化测试 - 处理多种输入
  3. 在边界处模拟 - 外部 API、数据库、文件系统
  4. 命名清晰描述 - 场景 + 预期结果
  5. 在 CI 中运行测试 - 每次推送都执行
  6. 监控覆盖率 - 但不要盲目追求 100%
  7. 删除跳过的测试 - 或修复它们
  8. 使用变异测试 - 验证测试质量
5
@534422530

已收录 7 个 Skill

相关推荐