HarmonyOS5 DevEco Test脚本生成:从OpenAPI文档自动创建Mock测试

154 阅读2分钟

以下为 ​​HarmonyOS 5基于OpenAPI文档自动生成Mock测试的完整方案​​,包含接口解析、Mock服务生成和自动化验证的代码实现:


1. 整体架构

image.png


2. OpenAPI解析器

2.1 接口模型提取

// openapi-parser.ets
class OpenAPIParser {
  static async parse(specPath: string): Promise<ApiModel[]> {
    const spec = await YamlLoader.load(specPath);
    return Object.entries(spec.paths).map(([path, methods]) => {
      return Object.entries(methods).map(([method, definition]) => ({
        path,
        method: method.toUpperCase(),
        parameters: definition.parameters || [],
        responses: definition.responses
      }));
    }).flat();
  }
}

// 示例:解析Swagger文档
const apis = await OpenAPIParser.parse('api/user-service.yaml');

2.2 响应模型生成

// response-generator.ets
class ResponseGenerator {
  static generateExample(responseDef: ResponseDef): any {
    if (responseDef.content?.['application/json']?.schema) {
      return this.generateFromSchema(
        responseDef.content['application/json'].schema
      );
    }
    return { status: 'success' };
  }

  private static generateFromSchema(schema: SchemaObject): any {
    if (schema.example) return schema.example;
    
    switch (schema.type) {
      case 'object':
        return Object.fromEntries(
          Object.entries(schema.properties || {}).map(([key, prop]) => 
            [key, this.generateFromSchema(prop)]
          )
        );
      case 'array':
        return [this.generateFromSchema(schema.items)];
      default:
        return this.getMockValue(schema.type);
    }
  }
}

3. Mock服务生成

3.1 动态路由创建

// mock-server.ets
class MockServer {
  private static routes: MockRoute[] = [];

  static createFromOpenAPI(apis: ApiModel[]) {
    apis.forEach(api => {
      this.routes.push({
        path: api.path,
        method: api.method,
        handler: (req) => this.handleRequest(api, req)
      });
    });
    
    HttpServer.registerRoutes(this.routes);
  }

  private static handleRequest(api: ApiModel, req: Request): Response {
    const statusCode = req.query.status || '200';
    const responseDef = api.responses[statusCode];
    
    return {
      status: parseInt(statusCode),
      body: ResponseGenerator.generateExample(responseDef)
    };
  }
}

3.2 异常场景模拟

// failure-mock.ets
class FailureInjector {
  static enableForRoute(path: string) {
    const route = MockServer.routes.find(r => r.path === path);
    if (route) {
      const originalHandler = route.handler;
      route.handler = (req) => {
        if (req.headers['x-failure-rate'] > Math.random()) {
          return { status: 503, body: { error: '服务不可用' } };
        }
        return originalHandler(req);
      };
    }
  }
}

4. 测试用例生成

4.1 自动化测试生成

// test-generator.ets
class TestCaseGenerator {
  static generate(api: ApiModel): TestCase[] {
    return Object.entries(api.responses).map(([status, responseDef]) => ({
      name: `${api.method} ${api.path} -> ${status}`,
      request: {
        method: api.method,
        path: api.path,
        params: this.generateParams(api.parameters)
      },
      expect: {
        status: parseInt(status),
        schema: responseDef.content?.['application/json']?.schema
      }
    }));
  }

  private static generateParams(params: ParameterObject[]): any {
    return params.reduce((acc, param) => {
      acc[param.name] = this.generateParamValue(param);
      return acc;
    }, {});
  }
}

4.2 参数生成策略

// param-generator.ets
class ParamGenerator {
  static generate(param: ParameterObject): any {
    switch (param.schema?.type) {
      case 'string':
        return param.schema.enum?.[0] || 
               `mock_${param.name}_${Math.random().toString(36).slice(2)}`;
      case 'number':
        return param.schema.minimum || 0;
      case 'boolean':
        return true;
      default:
        return null;
    }
  }
}

5. 自动化验证

5.1 响应验证器

// response-validator.ets
class ResponseValidator {
  static validate(res: Response, expected: ExpectedResponse): ValidationResult {
    const schema = expected.schema;
    if (!schema) return { valid: true };
    
    return {
      valid: this.validateSchema(res.body, schema),
      errors: this.findSchemaErrors(res.body, schema)
    };
  }

  private static validateSchema(data: any, schema: SchemaObject): boolean {
    // 使用JSON Schema验证库实现
    return JsonSchema.validate(data, schema);
  }
}

5.2 测试执行引擎

// test-engine.ets
class TestEngine {
  static async run(test: TestCase): Promise<TestResult> {
    const res = await HttpClient.request({
      method: test.request.method,
      url: test.request.path,
      params: test.request.params
    });
    
    const validation = ResponseValidator.validate(res, test.expect);
    
    return {
      name: test.name,
      passed: res.status === test.expect.status && validation.valid,
      response: res,
      errors: validation.errors
    };
  }
}

6. 完整工作流示例

6.1 从OpenAPI生成测试

// generate-tests.ets
async function generateAndRunTests() {
  // 1. 解析OpenAPI文档
  const apis = await OpenAPIParser.parse('api-spec.yaml');
  
  // 2. 生成Mock服务
  MockServer.createFromOpenAPI(apis);
  
  // 3. 创建测试用例
  const testCases = apis.flatMap(api => 
    TestCaseGenerator.generate(api)
  );
  
  // 4. 执行自动化测试
  const results = await Promise.all(
    testCases.map(tc => TestEngine.run(tc))
  );
  
  // 5. 生成报告
  return TestReport.generate(results);
}

6.2 带异常注入的测试

// failure-test.ets
describe('异常场景测试', () => {
  beforeAll(() => {
    FailureInjector.enableForRoute('/api/users');
  });

  it('应处理服务不可用错误', async () => {
    const res = await HttpClient.get('/api/users', {
      headers: { 'x-failure-rate': 1 }
    });
    
    expect(res.status).toBe(503);
    expect(res.body.error).toBe('服务不可用');
  });
});

7. 高级功能扩展

7.1 流量录制回放

// traffic-recorder.ets
class TrafficRecorder {
  static async recordAndGenerate(apiUrl: string) {
    const traffic = await ProxyServer.captureTraffic(apiUrl);
    return traffic.map(req => ({
      name: `${req.method} ${req.path}`,
      request: {
        method: req.method,
        path: req.path,
        params: req.query
      },
      expect: {
        status: req.response.status,
        body: req.response.body
      }
    }));
  }
}

7.2 智能参数变异

// param-mutator.ets
class ParamMutator {
  static generateInvalidParams(param: ParameterObject): any[] {
    const valid = ParamGenerator.generate(param);
    
    return [
      null,
      typeof valid === 'string' ? 123 : 'invalid_string',
      param.required ? undefined : valid
    ];
  }
}

8. 测试报告生成

8.1 可视化报告

// report-visualizer.ets
@Component
struct TestReport {
  @Prop results: TestResult[];
  
  build() {
    Column() {
      Text('API测试报告').fontSize(24)
      PieChart({
        data: [
          { label: '通过', value: this.results.filter(r => r.passed).length },
          { label: '失败', value: this.results.filter(r => !r.passed).length }
        ]
      })
      
      ForEach(this.results.filter(r => !r.passed), result => (
        Text(`${result.name}: ${result.errors?.join(',')}`)
          .fontColor('#ff0000')
      ))
    }
  }
}

8.2 CI集成配置

# .github/workflows/api-test.yml
jobs:
  api-mock-test:
    runs-on: harmonyos
    steps:
      - uses: harmonyos/openapi-test-action@v1
        with:
          spec: ./api-spec.yaml
          mock-port: 8080
      - name: Run Tests
        run: |
          ohpm install @test/mock-generator
          node generate-tests.ets

9. 关键生成规则

字段类型生成策略验证方法
string枚举值优先/随机字符串正则匹配/长度检查
number最小值/典型值范围验证
booleantrue/false轮换类型检查
object递归生成所有属性深度对比
array生成1-3个元素长度验证+元素类型检查

10. 完整示例输出

10.1 生成的Mock测试

// generated/user-api.test.ets
describe('用户服务API测试', () => {
  beforeAll(() => MockServer.start(3000));

  it('GET /users/1 应返回用户详情', async () => {
    const res = await HttpClient.get('/users/1');
    expect(res.status).toBe(200);
    expect(res.body).toMatchSchema({
      type: 'object',
      properties: {
        id: { type: 'number' },
        name: { type: 'string' }
      },
      required: ['id', 'name']
    });
  });

  it('POST /users 应创建新用户', async () => {
    const res = await HttpClient.post('/users', {
      name: 'test_user',
      age: 25
    });
    expect(res.status).toBe(201);
    expect(res.body.id).toBeDefined();
  });
});

10.2 Mock服务输出

// GET /users/1 响应示例
{
  "id": 1,
  "name": "张三",
  "email": "zhangsan@example.com"
}

通过本方案可实现:

  1. ​100%​​ OpenAPI接口自动覆盖
  2. ​零代码​​ 生成完整Mock服务
  3. ​智能​​ 异常场景注入
  4. ​可集成​​ 到CI/CD流水线