# UBC

UBC(User behavior is a use case), 用户行为即用例,全自动化无代码全用例平台!

# 背景-前端单元测试已渐入“魔道”

写单元测试用例的目的,是为了优化回归测试所消耗的测试资源。如果为了单测而单测—————消耗过多的开发资源,却无法获取到预期的收益,即犯了根本上的错误。

# 前置-关于前端单测

前端角度的单元测试,无非两个方向:

  • 面向代码(组件/类/函数)的单元测试;
  • 面向UI自动化的E2E(端到端)单元测试;

# 代码(组件/类/函数)的单元测试

覆盖率是片面的,面向业务逻辑的链路化测试逐渐占据工作量大头。

  • 技术选型

目前国内以jest框架及相关生态去做面向代码的单测已成主流。

  • 覆盖率用例方案

基于枚举参数的用例方案,来提升组件/类/函数的单测覆盖率。

  • 业务逻辑链路化测试方案

基于业务逻辑模拟业务组件的交互链路,以实现对完整业务链路的测试————接口/mock数据=>组件props植入=>组件内交互事件模拟=>验证返回值/UI

# E2E(端到端)单元测试

用代码实现用户在客户端的交互行为,对核心业务链路集成E2E单测,以提高在频繁的业务迭代中的业务保障。

  • 技术选型

谷歌官方提供的puppeteer框架对谷歌浏览器最为亲和,日前拥有star量已达到72k;而cypress凭借强大的生态和几乎全场景的demo库,是笔者的推荐首选;其他框架没真正玩过暂不评价。

  • 基于业务的链路化E2E单测方案

每一条用例,都应该是一条完整的业务链路。

我们拿典型的电商业务举例: 从登陆页进行表单填写后登陆=>路由到列表页浏览商品列表=>点击事件路由到详情页访问某条商品详情=>点击事件唤起购物车=>点击事件添加购物车=>点击事件唤起支付弹窗=>支付密码填写=>确认支付=>验证支付结果。 这就可以看作是一条相对完整的业务链路用例,而中间过程中的任何用户的可能性行为,都可以被视作另外的一条业务链路用例。 可以直接对标测试提供的冒烟用例集合。

# 现实—用例开发中的各种问题

从出发点讲,集成单测确实对业务的质量加持了保障。但现实往往充满了各种难题。

  • 片面追求覆盖率,致使大量的无意义单测增加了开发成本;
  • 开发的代码力不够,致使用例具有不稳定性;
  • 开发对业务链路的理解没到位,致使大量业务链路没有覆盖到;
  • 跨部门沟通成本过高,致使无法对标冒烟用例集锦;
  • 频繁的业务迭代,致使之前的用例失效,额外增加了迭代用例的成本;
  • 客户端的诸多环境难以全量覆盖, 致使用例虽然通过,但受端环境影响仍然挂掉。 …… 受这些因素的影响,前端单测战略逐步踏入“魔道”。

# UBC应景而生

为避免上述所有问题,我们期望存在这样一个系统=>能够自动化生成用例,自动化迭代用例,自动化运行用例。于是,UBC方案诞生了。

# 核心思考

  • 用户行为即用例

大量的用户本身对客户端的交互行为就是最真实有效的业务链路用例,如果能够收集全用户行为,即可以视作全业务链路覆盖率的冒烟用例集锦,将减少巨量的测试/开发资源的消耗;

  • userAgent提供了用例运行环境

就可以视作后续用例自动化运行的沙箱配置数据来源

# 技术方案

  • 交互事件埋点收集用户行为及客户端信息
  • 用例数据存储结构 => 以(处理后的用户数据&&事件id集合&&客户端环境)为唯一索引根源,每一段时间内的用户行为,即视为一条完整的用例,每次事件引发的(更新后的视图&&更新后的用户数据),即为断言的预期;
// 链路用例的数据结构
// TODO_待完善
{
    // 唯一caseId
    caseId: createHash(processedUserData,eventIdList, processedUserAgent),
    // 用例运行的沙箱环境配置,同步客户端
    caseEnvConfig: processedUserAgent, 
    // 处理后的用户数据,用来后续映射链路后的更新用户数据
    processedUserData,
    // 链路结束后,处理后的用户数据,是合成用例expect的参数
    // 这个每次事件发生后异步获取更新
    // 因此拿到的必然是最后的事件之后的数据
    processedUserDataCaseEnd,
    // 用以记录初始化状态的视图
    startRenderVdomTree,
    // 链路结束后的视图,是合成用例expect的参数
    // 这个每次事件发生后异步获取,
    // 因此拿到的必然是最后的事件之后的视图
    endRenderVdomTree,
    // 链路的开始时间,即链路事件的事件
    // 描述用例链路的交互事件集合
    caseLink: [{
        // 事件信息
        event: {
            // page-url
            router,
            // 轻量化处理后的事件参数,
            processedParams,
            // 
            id: createEventHash(router,processedParams),
        },
        // 触发时间,用来提供链路的事件之间的delay值。
        triggerTime,
    }]
}

关键点在于

1. 事件delay的常数值设置; 
2. event事件源抽象(这里要思考属于列表下的所有节点,是否有抽象为同一节点的必要?在设计之处就要做深入的思考。);
3. userData抽象;

# UBC架构图解

# 注解

  • UBC_服务

    1. 接受report,生成链路化测试case数据结构;
    2. 基于case数据结构,生成E2E代码;这步也可以放在沙箱运行时做;
    3. 入库存储匹配。
  • UBC_沙箱

    1. 技术选型参考E2E,Headless
    2. 为求与客户端环境的绝对同步,agent配置和项目中lock文件是必须的;(yarn/npm都可以,但不能用cnpm,因为cnpm不会生成lock文件)

# 关于UBC的重要补充

  • 目前看来,UBC的技术实现上没有不可攻克的难点,它可以抽离为完整的技术服务型产品。
  • 日活百万级别以下的业务,似乎没有太大的使用必要————UBC方案的case数据结构抽象,再怎么优化,烧钱是少不了的……
  • 笔者尚没有机会实践这个方案,即使有实践的业务场景,也难以将其收益量化。