Skip to content
On this page

自动化测试

全自动生成 API 自动化测试用例

介绍

诞生于我司领导的一个想法:实现全自动化测试公司产品。要求实现一下几个功能点:

  • 自动生成 API 测试用例并填充相应的参数
  • 自动化测试要满足一些常规手动测试条件,降低手写工作量
  • 可根据 JSDoc 中书写的信息生成特定的测试用例
  • 全平台(web、Android、iOS、小程序)测试结果比对
  • 不同版本间结果比对
  • 当前运行结果与上次运行结果比对

使用依赖

  • typescript 解析(ts-morph)
  • rollup 编译
  • typescript 编译(ts-node)
  • jest
  • @babel/generator、@babel/template、@babel/types

总体实现思路

第一步:解析 d.ts 文件

  1. 从原有的 d.ts 中读取到类型定义,包括 interfacenamespacetype 等信息

    使用 ts-morph 模块来做处理,使用 api 更加可控,可读性更高

    ts
    import { Project } from 'ts-morph';
    const project = new Project();
    const sourceFile = project.createSourceFile(filePath, sourceFileText, {
      overwrite: true,
    }); // 要使用 overwrite,因为如果 filePath 同名会报错。后面并不会对该文件进行修改
  2. 去递归处理生成的 sourceFile 文件,分别处理 ArrayObject 和基础类型等信息生成类型 TypeData[]

    ts
    export type BaseTypeDataString = 'string' | 'number' | 'boolean' | 'any' | 'void';
    export type ExtTypeDataString =
      | 'interface'
      | 'Function'
      | 'Union'
      | 'Array'
      | 'ArrayBuffer'
      | 'null';
    
    const baseTypeDatas: BaseTypeDataString[] = ['string', 'number', 'boolean', 'any', 'void'];
    
    interface BaseTypeData {
      type: BaseTypeDataString | ExtTypeDataString;
      doc?: DocsData;
      value?: string | number | boolean; // | null
    }
    
    export interface InterfaceTypeData extends BaseTypeData {
      type: 'interface';
      name: string;
      properties: Record<string, PropertyTypeData>;
    }
    
    export interface FunctionTypeData extends BaseTypeData {
      type: 'Function';
      params: ParamTypeData[];
      return: TypeData;
      key?: string;
    }
    
    export interface UnionTypeData extends BaseTypeData {
      type: 'Union';
      types: TypeData[];
    }
    
    export interface ArrayTypeData extends BaseTypeData {
      type: 'Array';
      elementType: TypeData;
    }
    
    export interface ArrayBufferTypeData extends BaseTypeData {
      type: 'ArrayBuffer';
    }
    
    interface OptionalTypeData extends BaseTypeData {
      key: string;
      optional: boolean;
    }
    
    export type TypeData =
      | BaseTypeData
      | InterfaceTypeData
      | FunctionTypeData
      | UnionTypeData
      | ArrayTypeData
      | ArrayBufferTypeData;
  3. 处理 docs 信息,读取 @autotest@uniPlatform 等信息来自定义生成的测试用例

第二步:生成测试用例

  1. 根据类型来定义测试应该如何生成:

    ts
    interface InputOptions {
      // 字符串填充数据
      string?: string[];
      // 数字填充数据
      number?: number[];
      // 布尔填充数据
      boolean?: boolean[];
      // any 填充数据
      any?: {
        number?: boolean;
        string?: boolean;
        boolean?: boolean;
        object?: boolean;
        array?: boolean;
        null?: boolean;
        undefined?: boolean;
      };
      object?: {
        // 填充可选属性
        optional?: boolean;
        // 对属性可选值作笛卡尔积
        product?: boolean;
        // 仅对相关属性作笛卡尔积
        relation?: boolean;
        // 移除 undefined 属性值
        removeUndefined?: boolean;
      };
      // 生成的数组长度
      array?: {
        min?: number;
        max?: number;
      };
      // 联合类型最大生成个数
      union?: number;
    }
    const defaultInputOptions = {
      string: ['', 'abc', '0', 'false', '', '/', '%', ';'],
      // Number.MIN_SAFE_INTEGER, , Number.MAX_SAFE_INTEGER
      number: [-1.1, -1, -0.1, 0, 0.1, 1, 1.1, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
      boolean: [false, true],
      any: {
        number: true,
        string: true,
        boolean: true,
        object: true,
        array: true,
        null: true,
        undefined: true,
      },
      object: {
        optional: true,
        product: true,
        relation: false,
        removeUndefined: false,
      },
      array: {
        min: 1,
        max: 1,
      },
      union: Number.MAX_SAFE_INTEGER,
    };
  2. 根据第一步生成的 TypeData[]InputOptions 来生成测试用例。需要做到以下几点:

    • 分别根据类型和配置来生成数据
    • 生成的数据要有一定的随机性
    • 生成的数据要满足一定的边界条件限制
    • 生成的数据要随机进行笛卡尔积组合(cartesianProduct)
    • 要可以根据 jsdoc 中配置的 relation 关系来进行数据组合生成

第三步:生成测试用例文件

  1. 根据第二步生成的测试用例使用 @babel/template 来生成 AST 数据
  2. @babel/types 在生成 AST 数据时填充具体的 AST 结构
  3. 使用 @babel/generator 和在 AST 中填充数据生成具体的 code
  4. 将 code 写入文件

总结

是一个很好的用来练手的项目。

  • 可以学习到很多 AST 的知识。学会使用 ts-morph 来处理 typescript AST,使用 @babel/generator@babel/template@babel/types 来生成 AST
  • 熟练 jest 的使用
  • 使用 rollup 配合 @rollup/plugin-replacerollup-plugin-terser@rollup/plugin-commonjsrollup-plugin-typescript2 等将项目编译成 npm
  • 使用 argparse 来将打包的文件进行 CLI

本文部分内容来自 ChatGPT、Github Copilot 自动生成