研究良久,终于发现了他代码写的快且bug少的原因

news2024/11/13 15:07:34

前言

读者诸君,今日我们适当放松一下,不钻研枯燥的知识和源码,分享一套高效的摸鱼绝活。

我有一位程序员朋友,当时在一个团队中开发Android应用,历经多次考核后发现:

在组内以及与iOS团队的对比中:

  • 他的任务量略多
  • 但他的bug数量和严重度均低
  • 但他加班的时间又少于其他人

不禁令人产生好奇,他是如何做到代码别的又快,质量又高的

经过多次研究我终于发现了奥秘。

为了行文方便我用"老L"来代指这位朋友。

最常见的客户端bug

“老L,听说昨晚上线,你又坐那摸鱼看测试薅别人,有什么秘诀吗?”

老L:“秘诀?倒也谈不上,你这么说,我倒是有个问题,你觉得平日里最常见的bug有哪些?”

“emm,编码上不健壮的地方,例如NPE,IndexOutOfBoundsException,UI上的可就海了去了,文本长度不一导致显示不下,间距问题,乱七八糟的一大堆”

老L:"哈哈,都是些看起来很幼稚、愚蠢的问题吧?是不是测试挂嘴边的那句:’ 你就不能跑一跑吗,你又不瞎,跑两下不就看到了,这么明显!!!’ "

我突然来了兴致,“你是说我们有必要上 TDD(test-driven-develop),按照DevOps思想,在CI(Continuous Integration)的时候,顺带跑自动化测试用例发现问题?”

老L突然打断了我:“不要拽你那些词了,记住了,事情是要人干的,机器只能替代可重复劳动,现在还不能替代人的主观能动性,拽词并不能解决问题。我们已经找到了第一个问题的答案,现在换个角度”


平日里最常见的bug有哪些?

  • 编码不健壮, 例如NPE,IndexOutOfBoundsException
  • UI细节问题, 例如文本长度不一导致显示不下,间距,等

为什么很浅显的问题没有被发现

问题来了

老L:“那么问题来了,为什么这些浅显的问题,在交测前没有被发现呢?”

我陷入了思考…

是开发们都很懒吗?也不至于啊!

是时间很紧来不及吗?确实节奏紧张,但也不至于不给调试就拿去测了!

“emm, 可能是迭代的节奏的太频繁,压力较大,并没有整块的时间用来自测联调”

问题不大

老L接过话茬,“假定你说的是正确的,那么就有两种可能。”

“第一种,自测与联调要比开发还要耗费心思的一件事情。但实际上,你我都知道,这一点并站不住脚!”

“而第二种,就是在开发阶段无法及时测试,拖到开发完,简单测测甚至被催促着就交差了”

仔细的思考后

  • 业务逐步展开,无法在任意时间自由地进行有效的集成测试
  • 后端节奏并不比前端快多少,在前端的开发阶段,难以借助后端接口测试,也许接口也有问题

“确实,这是一个挺麻烦的问题,听你一说,我感觉除了多给几天,开发完集中自测一波才行” 我如是说到。

没有问题

“NO NO NO”,老L又打断了我:“你想的过多了,你想借助一个可靠的、已经完备的后端系统来进行自测。对于你的需求来说,这个要求过高了,你这是准备干QA的活”

“我帮你列举一下情况”

  1. 一些数据处理的算法,这种没有办法,老老实实写单元测试,在开发阶段就可以做好,保障可靠性
  2. UI呢,我们现在写的代码,基本都做到了UI与逻辑分层,只要能模拟数据,就能跑起来看页面
  3. 业务层,后端逻辑我们无法控制,但 Web-API 调用的情况可以分析下并做一下测试,而对于返回数据的JSON结构校验、约束性校验也可以考虑做一下测试

总而言之,我们只需要先排除掉浅显的错误。而这些浅显的错误,属于情况2、3

老L接着说道:“你先歇歇吧,我来说,你再插嘴这文章就太长了!”

接下来就可以实现矛盾转移:“如何模拟数据进行测试”,准确的说,问题分成两个子问题:

  • 如何生成模拟数据
  • 如何从接缝中塞入数据,让系统得以使用

可能存在的接缝

问题来了

先看问题2:“如何从接缝中塞入数据,让系统得以使用”

脑暴一下,可以得出结论:

  • 应用内部
    • 替换调用web-api的业务模块,使用假数据调用业务链,一般替换Presenter、Controller实例
    • 替换Model层,不调用web-api,返回假数据或用假数据调用回调链
    • 侵入网络层实现,不进行实际网络层交互,直接使用假数据
    • 遵循切面,向缓存等机制模块中植入假数据
  • 应用外部
    • 使用代理,返回假数据
    • 假数据服务器

简单分析:

  • “假数据服务器” ,并且使用逻辑编造假数据的代价太大,过。
  • “使用代理,返回假数据”,可以用于特定问题的调试,不适用广泛情况,过。
  • “替换调用web-api的业务模块”,成本过大,过。
  • “替换Model层”,对项目的依赖注入管理具有较大挑战,备选,可能带来很多冗余代码。
  • “侵入网络层实现”,优选。
  • “向缓存等机制模块中植入假数据”,操作真实的缓存较复杂,但可以考虑增加一个 Mock缓存实现模块,基于SPI等机制,可以解决冗余代码问题,备选。

得出结论:

  • 方案1:“侵入网络层实现”,优选
  • 方案2:“替换Model层”,(项目的依赖注入做得很好时)作为备选,可能带来冗余代码
  • 方案3:“向缓存等机制模块中植入假数据”,增加一个 Mock缓存实现模块,备选。(基于SPI等机制,可以解决冗余代码问题)

再仔细分析: 方案1和方案3可以合并,形成一个完整的方案,但未必需要限定在缓存机制中

没有问题

OK 我们先搁置一下这个问题,看前一个问题。

全是问题

创造假数据

简单脑暴一下,无非三种:

  • 人工介入,手动编写 – 成本过大
    • 可能在前期准备好,基本是纯文本
    • 可能使用一个交互工具,在需要数据时介入,通过图形化操作和输入产生数据
  • 人工介入,逻辑编码
  • 基于反射等自省机制,并完全随机或者基于限制生成数据

“第一种代价过大,暂且抛弃”

"第二种可以采用,但是人力成本不容忽视! 一个可以说服我使用它的理由是:“可以精心设计单测数据,针对性的发现问题”

“第三种很轻松,例如使用Mockito,但生成合适的数据需要花费一定的精力”

我们来扒一扒第三种方式,其核心思想为:

  1. 获取类信息,得到属性集
  2. 遍历属性填充
  1. 基础类型、箱体类型,枚举,确定取值范围,使用Random取值,赋值
2. 普通类、泛型类,创建实例,回归步骤1
3. 集合、数组等,创建实例,回归步骤1,收集填充

不难得出结论,这一方法虽然很强大,但 创建高度定制化的数据 是一件有挑战的事情。

举个例子,模拟字符串时,一般会使用语料集作为枚举,进行取值。要得到“地址”、“邮箱”等特定风格的数据,需要结合框架做配置,客观上存在较高地学习、使用门槛。

你也知道,前几年我图好玩,写了个 mock库 。

必须强调的一点:“我并不认为我写的库比Mockito等库强大,仅仅是在我们开发人员够用的基础上,做到尽可能简单!”

你也知道,Google 在Androidx(前身为support)中提供了一套注解包: annotations。但Google并未提供bean validation 实现
,我之前也基于此做过一套JSR303实现,有一次突发灵感,这套注解的含义同样适用于 声明假数据取值范围 !!!

所以,我能使用它便捷的生成合适的假数据,在开发阶段及时的进行 “伪集成”


此刻,我再也忍不住要发言了:“且慢,老L,你这个做法有一定的侵入性吧。而且,如果数据类在不同业务下复用的话,是否存在问题呢?”

老L顿了顿,“确实,google的annotations是源码级注解,并不是运行时,我为了保持简单,使用了运行时反射而非代码生成。所以确实存在一定的代码侵入性”。

但是,我们可以基于此建立一套简单的MOCK-API,这样就不存在代码侵入了。

另外,也可以增加一套Annotation-Processor 实现方案,这样就可以适当沿用项目中的注解约束了,但我个人认为华而不实。


看你的第二个问题,Mocker一开始确实存在这个问题,有一次从Spring的JSR380中得到灵感,我优化了注解规则,这个问题已经被解决了。得空你可以顺着这个图看看:

Mocker.png

或者去看看代码和使用说明:https://github.com/leobert-lan/Mocker

再次审视如何处理接缝

此时我已经有点云里雾里,虽然听起来很牛,如何用起来呢?我还是很茫然,简直人麻了!不得不再次请教。

老L笑着说:“你问的是一个实践方案的问题,而这类问题没有银弹.不同的项目、不同的习惯都有最适宜的方法,我只能分享一下我的想法和做法,仅做参考”

在之前的项目中,我自己建了一个Mock-API,利用我的Mocker库,写一个假数据接口就是分分钟的事情。

测试机挂上charles代理,有需要的接口直接进行mapping,所以在客户端代码中,你看不到我做了啥。

当然,这个做法是在软件外部。

如果要在软件内部做,我个人认为这也是一个华而不实的事情。不过不得不承认是一件好玩的事情,那就提一些思路。

基于Retrofit的CallAdapter

public interface CallAdapter<R, T> {
    Type responseType();

    T adapt(Call<R> call);

    abstract class Factory {
        public abstract @Nullable
        CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
                              Retrofit retrofit);

        protected static Type getParameterUpperBound(int index, 
                                                     ParameterizedType type) {
            return Utils.getParameterUpperBound(index, type);
        }

        protected static Class<?> getRawType(Type type) {
            return Utils.getRawType(type);
        }
    }
}

很明显,我们可以追加注解,用以区分是否需要考虑mock;

可选:对于有可能需要mock的接口,可以继续追加切面,实现在软件外部控制使用 mock数据真实数据

而Retrofit已经使用反射确定了方法的 return Type ,在Mocker中也有适应的API直接生成假数据

基于Retrofit的Interceptor

相比于上一种,拦截器已经在Retrofit处理流程中靠后,此时在 Chain 中能够得到的内容已经属于Okhttp库的范畴。

所以需要一定的前置措施用于确定 “return Type”、“是否需要Mock” 等信息。可以借助Tag机制:

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Tag {
}

@GET("/")
Call<ResponseBody> foo(@Tag String tag);

最终从 Request#tag(type: Class<out T>): T? 方式获取,并接入mock,并生成 Response

其他针对Okhttp的封装

思路基本类似,不再展开。

写在最后

听完老L的思路,我若有所思,若有所悟。他的方案似乎很有效,而且直觉告诉我,这些方案中还有很多留白空间,例如:

  • 借用SPI等技术思路,可以轻易的解决 “Mock 模块集成与移除” 的问题
  • 提前外部控制是否Mock的接缝,可以在加一个工具APP、或者Socket+网页端工具 用以实现控制

但我似乎遗漏了问题的开始

问题来了

是否原意做 用于约束假数据生成规则的基础建设工作呢??? 例如维护注解

事情终究是人干的,人原意做,办法总比困难多。

最后一个小问题:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目请求url存在特殊字符 400错误

java项目请求url特殊字符 400错误 1 现象 请求路径带特殊字符&#xff0c;就会400错误&#xff0c;这就泄露了服务器版本和报错信息&#xff0c;无疑是敏感信息泄露&#xff0c;实属安全漏洞。 补充项目环境&#xff1a;springmvc、tomcat 8.5.59 2 原因 经排查和报错信息…

STM32--ADC模数转换器

学习江科大自化协stm32教程记录的笔记 ADC模数转换器 ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 DAC是数字-模拟转换器&#xff0c;但是P…

AI 助你轻松剪视频 # AutoCut

如果你还在犯愁每次剪视频都要反复听才能下手&#xff0c;不妨试试AutoCut , AI 大神李沐开源的一个剪辑神器&#xff0c;使用 Python 开发&#xff0c;它可以通过字幕来剪切视频。AutoCut 对你的视频自动生成字幕。然后你选择需要保留的句子&#xff0c;AutoCut 将对你视频中对…

C语言:变量的深入理解

文章目录一.什么是变量C语言中为什么要有类型&#xff1f;C语言中的类型为什么有这么多种呢&#xff1f;定义变量的本质为什么需要定义变量定义变量的本质定义变量时的规则二.深刻理解signed/unsigned定义的变量1.运算时的符号位2.数据的存储情况3.unsigned定义时的小细节三.大…

Android 13 VTS HIDL interface 解析

Android 13 VTS Introduction Android 13已经发布&#xff0c;VTS testcase发生很多变化&#xff0c;在此博客中对其每个测试项目进行流程介绍。 这里先对VTS 做一个介绍&#xff1a; VTS是vendor test suite简称&#xff0c;意为供应商测试套件。目的是确保Vendor层实现的兼容…

Spring Boot自动装配原理

Spring Boot自动装配原理1.Spring Boot 入口2.SpringBootApplicationSpringBootConfigurationComponentScanEnableAutoConfiguration判断自动装配开关是否打开获取EnableAutoConfiguration注解中的 exclude 和 excludeName获取需要自动装配的所有配置类最后3.总结1.Spring Boot…

Github访问量过百万!阿里内部至尊级分布式事务手册,实至名归

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说&#xff0c;就是一次大的操作由不同的小操作组成&#xff0c;这些小的操作分布在不同的服务器上&#xff0c;且属于不同的应用&#xff0c;分布式…

[附源码]JAVA毕业设计养生药膳推荐系统(系统+LW)

[附源码]JAVA毕业设计养生药膳推荐系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

[LeetCode周赛复盘] 第 323 场周赛20221211

[LeetCode周赛复盘] 第 323 场周赛20221211 一、本周周赛总结二、 [Easy] 6257. 删除每行中的最大值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6258. 数组中最长的方波1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6259. 设计内存分配器1. 题目描述2. 思路分析3. 代码…

web前端期末大作业【足球网页】学生网页设计作业源码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

基于C++实现(控制台)单位职工管理系统(数据结构)【100010017】

1需求分析 1.1 问题描述 对单位的职工进行管理&#xff0c; 包括插入、 删除、 查找、 排序等功能。 1.2 问题要求 职工对象数不必很多&#xff0c; 便于一次读入内存&#xff0c; 所有操作不经过内外存交换。 &#xff08;1&#xff09; 由键盘输 入职工对象&#xff0c;…

1564_AURIX_TC275_电压监控寄存器整理

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 1. 如果HSM保护开启了&#xff0c;那么访问修改其他的bit就会导致一个总线错误。 2. SMU中可以配置电压监控的相关alarm是否配置 生效。 这个寄存器是几个欠压阈值的配置&#xff0c;在这…

在Ubuntu中为ROG笔记本安装驱动asusctl

我是在Kubuntu22.04上安装的&#xff0c;系统自带“省电”、“平衡”、“性能”三个电源选项&#xff0c;显卡模式切换是拿nvidia驱动切换的&#xff0c;所以目前装的这个驱动我只用到了灯光调节功能。 文章目录介绍安装安装asusctl卸载显卡模式切换驱动supergfxctl使用方法启用…

LwIP——无操作系统启动流程

目录 启动流程 虚拟网卡控制块 发送流程 接收流程 总结 启动流程 通过阅读正点原子的无操作系统移植工程的源码&#xff0c;可以总结出LwIP的无操作系统的启动流程。 前面一些都是基于其他的外设的初始化&#xff0c;我们只关心这里lwip_comm_init()&#xff0c;这个函数的…

eve-ng ubuntu 20.04 设置iptables

eve-ng ubuntu 20.04 设置iptables一、设置方法1、建立iptables规则开机加载脚本2、建立iptables规则关机/重启保存脚本3、添加可执行权限4、保存当前iptables规则到配置文件二、命令解释一、设置方法 1、建立iptables规则开机加载脚本 sudo vi /etc/network/if-pre-up.d/ipt…

(十一)Vue之条件渲染

文章目录v-if与v-else-if、v-else一起使用v-show关于 <template>标签Vue学习目录 上一篇&#xff1a;&#xff08;十&#xff09;Vue之绑定样式 v-if v-if&#xff0c;默认为true&#xff0c;如果为false&#xff0c;底层实现是直接把这个节点删除 适用于&#xff1a;…

MLE,MAP,经验风险最小化,结构风险最小化,邻域风险最小化

文章目录Maximun likelihood extimationMaximum A Posterior EstimationVicinal Risk Minimization, VRMMaximun likelihood extimation 最大似然估计:样本->参数,完全相信观测到的数据,最优可能产生这些看到的数据的模型就是最理想的模型. 频率学派认为,参数是一个常数,不…

fpga实操训练(基础)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前我们虽然说过怎么编写verilog代码&#xff0c;怎么用iverilog软件和gtkwave软件进行仿真验证&#xff0c;但是一直没有说明如何在真实的板子上…

web前端期末大作业——基于HTML+CSS+JavaScript仿蘑菇街时尚服装购物商城

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

28个你应该知道的JavaScript技巧

今天我将分享一些Javascript中的常用技巧&#xff0c;以帮助您解决问题。设置过程中的常见问题更快更容易。 01、Javascript 反向字符串 下面是代码&#xff1a; /*niemvuilaptrinh.com*/const stringReverse str > str.split("").reverse().join("")…