JS SDK一体化测试方案的快速实践

449 阅读16分钟

作者:鲍潼、张靖宇 货拉拉/技术中心/质量保障部

一、背景

JavaScript SDK作为跨平台架构和模块化应用载体,其质量直接影响上层业务的可靠性与用户体验。从测试角度看,SDK 的测试工作在形式上是对其进行 “二次开发”,通过调用 API 模拟上层应用的真实调用方式,实现测试用例设计和执行。这里不仅要验证单接口在不同输入条件下的正确性,更要关注实际使用时的组合调用逻辑和时序关系,从功能测试、性能测试、稳定性和适配测试等多个维度进行全方位验证。由此也为测试人员带来了一系列挑战:

  • 测试场景复杂:SDK 应用场景涵盖单接口调用与复杂组合逻辑,接口间异步依赖与时序关系错综复杂,传统测试方式难以全面覆盖各类场景,易出现测试盲区。
  • 接口多、组合多:接口数量大且调用链路愈发复杂,各类测试全部依赖人工验证难以满足快速迭代的项目要求。
  • 代码编写负担重:测试代码编写工作复杂且重复性高,人工开发不仅耗时耗力,还容易出现逻辑错误,影响测试进度和质量。

二、整体架构

为系统性解决上述问题,我们聚焦全流程测试效能提升,搭建了JS SDK测试平台,通过分层设计与自动化能力集成,尝试为SDK测试构建更高效的测试方案,同时我们也尝试基于AI能力进行用例代码的生成,为测试能力的进一步提升探索方向。下文将详细拆解平台架构与功能逻辑 。

三、能力建设

3.1 框架搭建

为快速支撑第一期产品提测,构建高效、可扩展的测试方案,我们选择从架构层面进行统一规划,整合测试方案,实现测试资源的复用与测试效率提升。

  • 项目背景

    • 测试工程划分: 项目涉及功能、性能、接口测试,如果分别基于独立工程实现,将导致资源割裂,存在代码重复开发、测试用例重复维护的问题。各工程技术选型不统一,也将导致需要维护多套环境,资源投入成本高。
    • 测试版本管理: 测试对象缺乏统一管理标准,将导致测试对象不一致,测试节奏混乱,难以形成完整的体系保障。测试数据分散存储,也将增加维护工作的复杂度。
    • 测试环境管理: 多工程独立部署需重复执行环境搭建、部署,不同构件环境也将引入大量未知工作量。
  • 解决思路

    • 统一测试版本: 解决测试对象不一致问题,建立集功能、接口和性能测试的统一测试工程。

    • 一体化部署: 解决工程分散问题,减少重复开发、重复部署成本。

      • 统一测试工程编译,功能、性能、稳定性测试共享基础构件
      • 统一测试环境、统一部署
    • 统一交互入口: 提升测试体验、效率,支持测试渲染效果即时验证及对比测试。

  • 实现方案

创建统一代码库作为JS SDK测试的“壳”工程,在该工程中装载相关测试页面和测试用例,结构如下

js-sdk-test-platform/
├── src/
│   ……
│   ├── components/           # 全局组件
│   │   ├── ……
│   │   └── test/            # 测试通用组件
│   │
│   ├── views/                # 页面组件
│   │   ├── testFunctional/  # 功能测试目录
│   │   │   ├── testCases/  #   装载测试用例
│   │   │   ├── index.vue   #   测试页面入口
│   │   ├── testPerformance/ # 性能测试用例
│   │   ├── testAutomation/   # 自动化框架目录
│   │   ├── testSmart/       # 扩展能力
│   │   ├── testReport/      # 测试报告页面
│   │   │    ……
│   │
├── playwright/              
│   ……
│   ├── tests/                
│   │   ├── functional/      # 功能测试执行脚本
│   │   ├── performance/     # 性能测试执行脚本
│   ├── reports/              # 测试数据、报告目录
│   │
│   ├── App.vue               # 应用根组件
│   └── main.ts               # 应用入口

项目使用成熟的 Vue 项目打包方案,确保代码在多环境下稳定运行;发布环节则基于镜像构建流程,结合轻量化部署策略,提升项目部署效率并降低维护成本。基于这个工程,各类型测试代码有了规范的管理方式,为相关测试的铺开提供了基本保障。

3.2 功能测试

功能测试本质上是基于API 的二次开发实践,围绕业务测试场景需求,通过设计代码架构和编写测试用例,实现测试功能的高效实现和验证。结合上面的壳工程,我们快速集成了功能测试模块,核心步骤如下:

  • 需求分析

     在项目初期,只有接口文档及研发提供的Demo工程作为输入,这为测试工作开展带来了不小的困难,为此,我们首先对当时的功能测试进行了初步摸底。  

    • 测试对象: 经过对测试对象进行分析梳理,最终整理出 8 大核心测试对象类,涵盖 120 余项待测试接口方法。
    • 测试工程: 在开发环节,虽已存在一套自测 Demo,但仅局限于接口核心功能验证,存在测试场景单一、缺乏性能与兼容性测试等局限。
    • 测试方法: 缺少异常测试,需补充异常输入测试,模拟非法参数、数据越界等极端情况;引入多环境兼容性测试,覆盖不同浏览器、操作系统及设备类型。
  • 实现方案

通过将测试入口页面、测试用例文件拆分,实现了用例统一管理,通过用例加载组件控制,实现用例高效加载和测试对象复用。

const allOptionsPath =
        import.meta.glob('./*.ts', {
                eager: true
        });  //读取测试用例ts文件

const allOptions: any = Object.keys(allOptionsPath).map((path) => {
        // eslint-disable-next-line prefer-object-spread
        return Object.values(Object.assign({}, allOptionsPath[path]))[0];
});

allOptions.sort((a: any, b: any) => {
        if (a.groupName === b.groupName) {
                return a.label.length - b.label.length;
        }
        return a.groupName.localeCompare(b.groupName);
});

export const MENU_OPTIONS = [{
                label: '分组一',
                key: 'comObj',
                children: allOptions.map((item: any) => ({
                        label: item.label,
                        key: `comObj_${item.key}`,
                })),
        },
        {
                label: '分组二',
                key: 'testObj',
                children: allOptions.map((item: any) => ({
                        label: item.label,
                        key: `testObj_${item.key}`,
                })),
        },
];

通过以上方式,实现前端测试页面与测试代码文件分离,提高管理效率,用例清晰明了,如下:

export default {
  label: '基础操作',
  key: 'baseOptions',
  groupName: 'base',
  children: [
    {
      label: 'zIndex 减 5',
      key: 'declineZIndex',
      methods(controller: IMapController) {
        checkAndDoAction(controller, (marker) => {
          const currentZIndex = marker.getZIndex();
          if (typeof currentZIndex === 'number') {
            let newZIndex = currentZIndex - 5;
            newZIndex = newZIndex >= 0 ? newZIndex : 0;
            marker.setZIndex(newZIndex);
            message.success(`${this.label} 后为: ${newZIndex}`);
          } else {
            message.error('无法获取当前的 zIndex');
          }
        });
      },
    },
   ...
  ]
}

同时结合前端UI组件,实现了用例快速分组。如图:

  • 实践效果

      完成以上功能测试架构改造后,我们在第一次提测过程当中,沉淀了大量测试用例,在后续的测试中持续体现价值:

    • 用例沉淀: 建立了 14个可复用测试组件库,标准化的测试架构可实现高效复用。
    • 测试覆盖: 截止目前,近 500个测试用例已覆盖 8个类的 120个功能接口,接口覆盖率达到96%。

这些沉淀不仅提升了当前提测的测试效率与质量,更为将来的技术迭代和需求更新奠定了坚实基础。

3.3 接口自动化测试

功能测试的改善解决了组合场景和效果验证的问题,而核心功能的快速验证与冒烟测试则通过单接口的自动化测试来进行补充。

  • 建设思路:

    • 单接口基础功能验证:通过梳理用例,对核心接口的参数校验、边界测试及返回值进行验证,确保原子能力的稳定。
    • 核心组合场景 设计:分析应用场景,设计多接口组合验证用例,覆盖核心同步与异步调用的时序关系和业务逻辑。
    • 异常数据 Mock 验证:注入网络异常、非法数据等非常规场景,测试极端条件下的错误处理机制与稳定性。
  • 技术选型

    • 测试框架JasmineJestMocha
      用例可读性
      断言库自带自带需引入Chai
      初始化配置简单开箱即用复杂
      集成度
      浏览器运行原生支持扩展支持灵活支持
      最终实现复杂度
      最终选型✅ 采用❌ 淘汰❌ 淘汰

由于被测试对象涉及图像渲染,大量用例需要在前端浏览器中运行,Jest框架需额外集成其他工具实现运行时效果验证,成本较高。Mocha虽支持浏览器执行,但由于其集成度不高,没有内置断言库,且初始化配置较复杂。Jasmine则语法简洁、易于上手且自带断言库,可在浏览器内加载运行,对于我们的测试项目更为适配,能够以更低成本快速落地。

  • 实现方案

针对图像渲染及交互类接口 ,执行测试用例是会进行页面渲染和对象操作,测试人员只需要关注测试对象的调用即可,用例清晰明了:

describe('baseMap Test suite', function () {
  let mapmaps: IMapmap[];
  it('setCenter(),getCenter()', function (done) {
    const promises = mapmaps.map(async (map) => {
      map.setCenter({ lng: 116.4549, lat: 39.9159 });
      await utils.sleep(1000);
      expect(map.getCenter().lng).toBeCloseTo(116.4549, 3);
      expect(map.getCenter().lat).toBeCloseTo(39.9159, 3);
    });
    Promise.all(promises)
      .then(() => done())
      .catch(done.fail);
  });

})

针对数据检索与解析类接口,使用 MSW(Mock Service Worker)作为接口模拟层,实现 API 请求拦截与响应伪造。通过动态配置响应数据,模拟网络延迟、错误等异常场景,使异常测试场景摆脱对真实后端服务的依赖。

import { http, HttpResponse } from 'msw';
import { SetupWorker, setupWorker } from 'msw/browser';

const worker: SetupWorker = setupWorker();

it('searchPoi(),完整参数透传(mock)', async function () {
      //Mock请求数据
      const expectedBodyParams = {
        kw: '火车站',
        city: '北京',
        city_id: '10**',
        lon: 116.39****,
        lat: 39.90****,
      };
      
      const proxy = http.post(
        `https://${testURL}`,
        async ({ request, params, cookies }) => {
          console.log('request: ', request);
          // 校验请求参数
          const urlParams = new URLSearchParams(request.url.split('?')[1]);
          expect(urlParams.get('params1')).toBe('10****');
          expect(urlParams.get('params2')).toBe('testParams');

          // 解析请求体并检查
          const requestBody = await request.json();
          expect(requestBody).toEqual(expectedBodyParams);
          // Mock 响应数据
          return HttpResponse.json(
            {
              ret: -9,
              msg: 'Mocked response',
            },
            {
              status: 200,
              statusText: 'Mocked status',
            }
          );
        }
      );
      // 启动mock服务器
      await worker.start();
      worker.use(proxy);
      try {
        const res = await searchPoi({
          queryParams: { params1: '10****', params2: 'testParams' },
          bodyParams: expectedBodyParams,
        });
      } catch (err) {
        // 校验异常错误
        expect(err.message).toBe('错误码:-9 ,错误信息:Mocked response');
      } finally {
        // 关闭mock服务器
        await worker.stop();
      }
    });
  • 实践效果

    • 接口覆盖: 自动化测试用例积累230+,检查点超1000个,已覆盖8个类、114个接口,接口覆盖率达到91.2%
    • 执行效率: 执行测试人力成本从 0.5人日提效至 10分钟

3.4 性能测试

性能测试用例设计: 分析SDK性能损耗接口,设计性能测试场景,并结合功能测试框架,快速实现性能脚本执行组件

export default {
  label: '性能测试',
  key: 'performance',
  groupName: 'performancebase',
  children: [
    {
      label: '连续 setZoom 100次', // 性能测试场景
      key: 'setZoom',
      methods: (obj) => { // 性能测试步骤
        const currentZoom = obj.getZoom();
        let count = 0;
        const t = setInterval(() => {
          setZoom(obj);
          count += 1;
          if (count >= 100) {
            clearInterval(t);
            obj.setZoom(currentZoom, 1000);
            testEnd();
          }
        }, 1000);
      },
    },
    ...
  ]
}

  数据采集能力: 引入 Stats.js 工具,实时、精准进行性能数据采集,实现帧率波动、内存占用变化等核心指标的动态监测与可视化展示。

import Stats from 'Stats.js';

const stats = new Stats();
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild(stats.dom);
// 统计信息显示在右上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.right = '10px';
stats.domElement.style.top = '80px';

function animate() {
  stats.begin();
  // monitored code goes here
  // console.log( 'stats.start' );
  stats.end();
  requestAnimationFrame(animate);
}

  自动化执行与数据采集: 借助 Playwright 的自动化能力,构建从测试执行到数据采集的全流程自动化链路,实现性能测试的高效调度与数据保存。

  • 实践效果

    • 测试能力 累计沉淀核心性能测试场景 24个,实现性能数据的纵向对比,快速感知性能变化趋势。
    • 测试效率 自动化测试用例执行和性能数据采集,人力投入从 0.5人日提升至分钟级。

3.5 智能测试代码生成

当前AI技术已具备强大的代码理解和生成能力,而我们的SDK有着丰富详细的接口文档。能否让AI充当"测试开发助手",根据需求自动生成可执行代码?这或许能大幅降低编写测试用例的门槛,让测试人员更聚焦于场景设计而非代码实现。

  • 为什么需要智能生成?

    • 场景构建效率低: 当前功能测试需手工编写SDK调用代码,但不同平台(Android/iOS/Harmony/JS等)实现差异大,开发成本高。
    • 技术门槛限制: 多端SDK要求测试人员掌握不同语言技术栈,团队能力难以全面覆盖。
    • 重复劳动严重: 基础功能测试(如初始化、参数校验)存在大量模式化代码,人工编写耗时易错。
  • 验证目标(JS SDK试点)

    • 自然语言转代码: 通过描述测试场景直接生成可执行的JS代码。
    • 降低技术依赖: 让非专业开发者也能快速构建测试用例。
    • 轻量级快速接入: 直接复用现有服务平台和内部部署的DeepSeek API,避免额外部署。
  • 技术选型

    为了确保智能测试代码生成系统的稳定性和扩展性,我们对关键技术模块进行了体验、评估,结合我们项目需要最终选型如下:

    • 意图识别与函数映射
      评估维度DeepSeek Function Calling传统正则匹配开源NLP模型+规则引擎
      核心能力自然语言→SDK接口精准匹配关键词触发固定代码片段有限泛化能力,需持续维护规则库
      实现复杂度低(API直接调用)中(需编写大量正则)高(需训练+调优模型)
      扩展成本仅需更新tools清单每新增接口需新增较多正则需重新标注数据+微调模型
      最终选型✅ 采用❌ 淘汰❌ 淘汰
    • 上下文优化管理
      评估维度Few-shot Learning纯Prompt工程向量数据库检索
      核心能力通过示例引导输出规范依赖超长Prompt描述相似案例检索
      响应速度<100ms200~500ms(Prompt膨胀)300ms+(含检索耗时)
      代码规范符合率较高较高依赖检索质量
      历史偏好支持支持(LRU缓存)不支持支持(需额外处理)
      最终选型✅ 采用❌ 淘汰❌ 淘汰
  • 实现方案

本项目采用前后端分离架构,基于现有技术栈快速落地,兼顾性能与扩展性。提供RESTful API接口,支持 SSE流式输出,动态返回AI生成的代码片段。通过HTTP Client调用内部DeepSeek V3 API,处理Function Calling的请求/响应转换。

  • 执行逻辑:

  • 核心模块:

{
  "type": "function",
  "function": {
  // 对每个函数进行定义
    "name": "addText",
    "description": "添加一个文本标注,支持内容、位置设置,并可控制其样式和交互行为",
    "parameters": {
      "type": "object",
      "properties": {
        // 对函数里每个参数进行定义
        "position": {
          "type": "object",
          // 对每个函数进行自然语言的提示与限制
          "description": "文本标注的经纬度坐标,格式为 { lng: 116.391447, lat: 39.907325 },lng和lat不需要加双引号"
        },
        ......
        "fontSize": {
          "type": "string",
          "description": "字体大小(可选),如 '20px'"
        }
      },
      "required": ["position", "content"]
    }
  }
}
MessageBuilder builder = new MessageBuilder();
builder.addSystemMessage(system2)
 //添加多个上下文记忆,使代码风格统一且格式规范
        .addUserMessage("需求:定位3次北京随机位置,回调函数:[{"index":0,"id":"call_0_dded1fc4-bc10-401f-ac3e-4b2e260cc5f0"...}}]}]")
        .addAssistantMessage("```javascript\n // 第一次定位到北京随机位置 (116.404, 39.915)\n const position1 = { lng: 116.404, lat: 39.915 };\n // 第二次定位到北京随机位置 (116.408, 39.918)\n const position2 = { lng: 116.408, lat: 39.918 };\n // 第三次定位到北京随机位置 (116.412, 39.920)\n const position3 = { lng: 116.412, lat: 39.920 };\n map.setCenter( position1,500);\n await utils.sleep(500);\n map.setCenter( position2,500);\n await utils.sleep(500);\nmap.setCenter( position3,500);\n```")
        .addUserMessage("需求:遍历的方法随机移动和放大缩小多个地点。")
        .addAssistantMessage("```javascript\n// 定义多个缩放和移动的目标位置\n const locations = [\n { zoom: 5, lng: 116.404, lat: 39.915, duration: 1000 },   // 北京\n { zoom: 5, lng: -74.006, lat: 40.7128, duration: 1500 },  // 纽约\n { zoom: 5, lng: 139.6917, lat: 35.6895, duration: 800 }  // 东京\n ];\n  // 遍历每个位置并执行缩放和移动\n for (const loc of locations) {\n await map.setZoomAndCenter(loc.zoom, { lng: loc.lng, lat: loc.lat }, loc.duration);\n await utils.sleep(loc.duration);\n }```")
        .addUserMessage(userRequest2);
  • 服务搭建

    • 服务采用Spring Boot作为核心,基于现有的Java服务项目框架,整合FastJSON进行数据解析,使用RestTemplate发起对DeepSeek V3 大模型API的请求,并通过SseEmitter实现前端的流式响应和页面交互。
    • 服务设计遵循“工具函数即插即用”的原则。当需要适配新的平台(如 Android/iOS SDK)时,只需更新 更新tools配置,无需修改核心业务逻辑,提升系统的灵活扩展性。
  • 业务流程:

  • 应用效果

测试人员根据不同的业务或质量场景输入自然语言需求,后端服务携带预置的tools清单(含SDK函数签名)调用DeepSeek V3,模型通过Function Calling精准匹配到需求所需的函数方法,并配置需求对应参数,自动填充缺省值,通过SSE流式返回带语法高亮的可执行代码

  • 示例:复杂参数构造场景

    • 输入:输入操作需求
    • 输出:AI 根据需求输出对应的可执行代码

  • 项目收益:

    • 非开发人员也可快速生成有效测试代码,新人上手时间极大缩短,每条基础用例对比人工编写缩短 10分钟。
    • 35个基础场景(如初始化、参数校验),可实现零修改直接执行。
    • 可支持JS SDK 7个大类 61个接口基础操作(图像渲染、覆盖物、事件监听等)用例代码生成。
  • 探索结论

    • 技术可行性: 本次验证证明,在具备规范接口文档的前提下,AI驱动的基础测试代码生成已达到生产可用水平

    • 简单场景: 符合测试测试需要,接口调用清晰、规范

    • 复杂场景:

      • 能力实现:基本按照需求实现可执行代码

      • 能力欠缺:对描述不明确的指令可能生成泛化代码,需结合追问澄清机制

四、未来规划

随着项目的第一期提测,我们逐步完成了这套 JS SDK测试方案的搭建,实现了初步的测试能力支撑,其首要意义还是在于快速支持需求交付。面向未来,平台能力和测试能力仍存在诸多可提升之处。

  • 平台能力方面: 补充任务化管理,实现任务自动分配与进度跟踪,提升协同效率。打造用例在线编辑功能,方便测试人员即时修改,灵活响应各类变更。
  • 测试能力方面: 建设持续集成能力,将已有的测试能力体现在每一次代码提交,完善覆盖率统计,清除测试盲点,提升测试质量。
  • 智能化测试方面:依托大模型技术,增强对复杂需求理解,优化模糊指令生成效果。构建知识库,汇聚业务及测试数据,为复杂场景代码生成提供更可靠的支撑。