Skip to content

API 测试策略

全面的 API 测试方法和实践

API 测试金字塔

API 测试应该遵循测试金字塔原则,确保快速反馈和全面覆盖:

API 测试类型

1. 功能测试

验证 API 的业务逻辑和功能正确性。

测试要点

  • 正常流程: 验证预期输入产生正确输出
  • 边界条件: 测试极限值和边界情况
  • 异常处理: 验证错误场景的处理
  • 业务规则: 确保业务逻辑正确实现

示例:用户注册 API 测试

java
@Test
@DisplayName("用户注册功能测试")
class UserRegistrationApiTest {
    
    @Test
    void shouldRegisterUserSuccessfully() {
        // Given
        RegisterRequest request = RegisterRequest.builder()
            .email("test@example.com")
            .password("SecurePass123!")
            .username("testuser")
            .build();
        
        // When
        Response response = given()
            .contentType(ContentType.JSON)
            .body(request)
            .when()
            .post("/api/users/register");
        
        // Then
        response.then()
            .statusCode(201)
            .body("id", notNullValue())
            .body("email", equalTo("test@example.com"))
            .body("username", equalTo("testuser"));
    }
    
    @Test
    void shouldRejectDuplicateEmail() {
        // Given - 创建已存在的用户
        createUser("existing@example.com");
        
        RegisterRequest request = RegisterRequest.builder()
            .email("existing@example.com")
            .password("Password123!")
            .username("newuser")
            .build();
        
        // When & Then
        given()
            .contentType(ContentType.JSON)
            .body(request)
            .when()
            .post("/api/users/register")
            .then()
            .statusCode(409)
            .body("error", equalTo("Email already exists"));
    }
}

2. 性能测试

评估 API 的响应时间、吞吐量和资源使用。

关键指标

  • 响应时间: P50、P95、P99 延迟
  • 吞吐量: 每秒请求数 (RPS)
  • 并发能力: 同时处理的请求数
  • 资源使用: CPU、内存、网络

JMeter 测试计划

xml
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="API Performance Test">
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
        <collectionProp name="Arguments.arguments">
          <elementProp name="BASE_URL" elementType="Argument">
            <stringProp name="Argument.name">BASE_URL</stringProp>
            <stringProp name="Argument.value">https://api.example.com</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="User API Load Test">
        <intProp name="ThreadGroup.num_threads">100</intProp>
        <intProp name="ThreadGroup.ramp_time">30</intProp>
        <longProp name="ThreadGroup.duration">300</longProp>
      </ThreadGroup>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

Gatling 性能测试

scala
class ApiPerformanceSimulation extends Simulation {
  
  val httpProtocol = http
    .baseUrl("https://api.example.com")
    .acceptHeader("application/json")
    
  val userScenario = scenario("User API Performance Test")
    .exec(
      http("Get User")
        .get("/api/users/${userId}")
        .check(status.is(200))
        .check(responseTimeInMillis.lessThan(500))
    )
    .pause(1, 3)
    .exec(
      http("Update User")
        .put("/api/users/${userId}")
        .body(StringBody("""{"name": "Updated Name"}"""))
        .check(status.is(200))
    )
    
  setUp(
    userScenario.inject(
      rampUsersPerSec(1) to 100 during (30 seconds),
      constantUsersPerSec(100) during (5 minutes)
    )
  ).protocols(httpProtocol)
   .assertions(
     global.responseTime.percentile(95).lessThan(500),
     global.successfulRequests.percent.greaterThan(99)
   )
}

3. 安全测试

确保 API 的安全性和数据保护。

测试清单

  • 认证测试: JWT、OAuth2、API Key
  • 授权测试: 角色和权限验证
  • 输入验证: SQL 注入、XSS 防护
  • 加密传输: HTTPS、TLS 版本
  • 速率限制: 防止 DDoS 攻击

OWASP ZAP 自动化测试

python
from zapv2 import ZAPv2
import time

# 配置 ZAP
zap = ZAPv2(apikey='your-api-key')
target = 'https://api.example.com'

# 启动扫描
print(f'开始扫描 {target}')
scan_id = zap.ascan.scan(target)

# 等待扫描完成
while int(zap.ascan.status(scan_id)) < 100:
    print(f'扫描进度: {zap.ascan.status(scan_id)}%')
    time.sleep(5)

# 获取结果
alerts = zap.core.alerts(baseurl=target)
for alert in alerts:
    print(f"风险级别: {alert['risk']}")
    print(f"警告: {alert['alert']}")
    print(f"描述: {alert['description']}")
    print(f"解决方案: {alert['solution']}")
    print("-" * 50)

4. 契约测试

验证 API 提供者和消费者之间的契约。

Pact 契约测试

javascript
// 消费者端测试
const { Pact } = require('@pact-foundation/pact');
const { getUserById } = require('./userClient');

describe('User API Contract', () => {
  const provider = new Pact({
    consumer: 'Web Frontend',
    provider: 'User Service',
    port: 8080
  });

  beforeAll(() => provider.setup());
  afterAll(() => provider.finalize());

  describe('get user by id', () => {
    it('should return user details', async () => {
      // 定义期望的交互
      await provider.addInteraction({
        state: 'user with id 123 exists',
        uponReceiving: 'a request for user 123',
        withRequest: {
          method: 'GET',
          path: '/api/users/123',
          headers: {
            'Accept': 'application/json'
          }
        },
        willRespondWith: {
          status: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            id: '123',
            name: 'John Doe',
            email: 'john@example.com'
          }
        }
      });

      // 执行测试
      const user = await getUserById('123');
      
      expect(user).toEqual({
        id: '123',
        name: 'John Doe',
        email: 'john@example.com'
      });
    });
  });
});

API 测试最佳实践

1. 测试数据管理

yaml
# test-data.yaml
users:
  valid:
    - email: "test1@example.com"
      password: "Password123!"
      username: "testuser1"
  invalid:
    - email: "invalid-email"
      password: "short"
      username: ""

2. 环境隔离

javascript
// config/test-environments.js
module.exports = {
  development: {
    baseUrl: 'http://localhost:3000',
    database: 'test_db_dev'
  },
  staging: {
    baseUrl: 'https://staging-api.example.com',
    database: 'test_db_staging'
  },
  production: {
    baseUrl: 'https://api.example.com',
    database: 'test_db_prod'
  }
};

3. 测试自动化框架

python
# tests/base_api_test.py
import pytest
import requests
from datetime import datetime

class BaseAPITest:
    """API 测试基类"""
    
    @pytest.fixture(autouse=True)
    def setup(self):
        """测试前准备"""
        self.base_url = os.getenv('API_BASE_URL', 'http://localhost:8080')
        self.session = requests.Session()
        self.test_data = []
        
    def teardown(self):
        """测试后清理"""
        for data in self.test_data:
            self.cleanup_test_data(data)
            
    def create_test_user(self, **kwargs):
        """创建测试用户"""
        user_data = {
            'email': f'test_{datetime.now().timestamp()}@example.com',
            'password': 'TestPass123!',
            'username': f'testuser_{datetime.now().timestamp()}'
        }
        user_data.update(kwargs)
        
        response = self.session.post(
            f'{self.base_url}/api/users',
            json=user_data
        )
        
        user = response.json()
        self.test_data.append(('user', user['id']))
        return user

4. 测试报告

html
<!-- API 测试报告模板 -->
<!DOCTYPE html>
<html>
<head>
    <title>API 测试报告</title>
    <style>
        .summary { background: #f5f5f5; padding: 20px; }
        .passed { color: #4caf50; }
        .failed { color: #f44336; }
        .metrics { display: flex; gap: 20px; }
        .metric-card { 
            background: white; 
            padding: 15px; 
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
    </style>
</head>
<body>
    <div class="summary">
        <h1>API 测试执行报告</h1>
        <p>执行时间: {{ execution_time }}</p>
        <div class="metrics">
            <div class="metric-card">
                <h3>总测试数</h3>
                <p>{{ total_tests }}</p>
            </div>
            <div class="metric-card passed">
                <h3>通过</h3>
                <p>{{ passed_tests }}</p>
            </div>
            <div class="metric-card failed">
                <h3>失败</h3>
                <p>{{ failed_tests }}</p>
            </div>
        </div>
    </div>
</body>
</html>

持续测试集成

Jenkins Pipeline

groovy
pipeline {
    agent any
    
    stages {
        stage('API Tests') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm run test:unit'
                    }
                }
                
                stage('Integration Tests') {
                    steps {
                        sh 'npm run test:integration'
                    }
                }
                
                stage('Contract Tests') {
                    steps {
                        sh 'npm run test:contract'
                    }
                }
            }
        }
        
        stage('Performance Tests') {
            when {
                branch 'main'
            }
            steps {
                sh 'npm run test:performance'
            }
        }
        
        stage('Security Scan') {
            steps {
                sh 'npm run test:security'
            }
        }
    }
    
    post {
        always {
            publishHTML([
                allowMissing: false,
                alwaysLinkToLastBuild: true,
                keepAll: true,
                reportDir: 'test-reports',
                reportFiles: 'api-test-report.html',
                reportName: 'API Test Report'
            ])
        }
    }
}

测试工具选择

工具类型推荐工具适用场景
REST APIPostman, REST Assured功能测试、集成测试
性能测试JMeter, Gatling, K6负载测试、压力测试
安全测试OWASP ZAP, Burp Suite漏洞扫描、渗透测试
契约测试Pact, Spring Cloud Contract微服务契约验证
Mock 服务WireMock, MockServer依赖服务模拟

总结

成功的 API 测试策略需要:

  1. 🎯 全面覆盖: 功能、性能、安全、契约
  2. 🔄 持续执行: 集成到 CI/CD 流程
  3. 📊 清晰报告: 可视化测试结果
  4. 🛠️ 合适工具: 选择适合的测试工具
  5. 🚀 自动化优先: 减少手动测试工作

通过实施完整的 API 测试策略,确保 API 的质量、性能和安全性。

SOLO Development Guide