💠

💠 2024-10-17 10:43:43


测试

参考: 测试的道理 参考: Everything You Need to Know About Software Testing Methods

为何要使用测试

  1. 帮助理解需求
    • 单元测试应该反映Use Case,把被测单元当成黑盒测试其外部行为。
  2. 提高实现质量
    • 单元测试不保证程序做正确的事,但能帮助保证程序正确地做事,从而提高实现质量。
  3. 测试成本低
    • 相比集成测试、验收测试,单元测试所依赖的外部环境少,自动化程度高,时间短,节约了测试成本。
  4. 反馈速度快
    • 单元测试提供快速反馈,把bug消灭在开发阶段,减少问题流到集成测试、验收测试和用户,降低了软件质量控制的成本。
  5. 利于重构
    • 由于有单元测试作为回归测试用例,有助于预防在重构过程中引入bug。
  6. 文档作用
    • 单元测试提供了被测单元的使用场景,起到了使用文档的作用。
  7. 对设计的反馈
    • 一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。

基础知识

码农翻身: 张大胖和单元测试

参考: Unit testing code with a file system dependency如何编写脱离外部依赖的真正单元测试

测试替身

泛指任何出于测试目的的替换真实对象的假冒对象,为了解决测试代码的依赖项问题。
四种方式: 虚设,伪装,存根,模拟

p286

虚设对象

  • 他是用来填充参数列表,填补那些总也不会用的必填域。大多数情况下,甚至可以传入null,就是把需要的参数虚拟出来
    • 只是为了避免NPE,让代码能跑起来

存根对象

  • 用来代替真实环境对象,需要有做出相同响应的对象,就是存根对象
    • 将一些外部类或三方资源,写一个类,以及对应的方法,来提供测试运行

伪装替身

  • 伪装替身,可以看作是存根的升级。他做的工作几乎是和生产代码是一样的,单位了满足测试需求也会使用便捷的方式
    • 例如 内存数据库HSQLDB 的使用,

模拟对象 Mock

  • 存根对象的调用通常会返回形同的结果。所以不能模拟任何与状态有关的行为,模拟对象就能够更好的胜任

    • 在准备要用的模拟对象时,告诉他会有哪些调用,以及对应的相应,模拟会和DI结合更好。可以用一个虚拟的对象,这个对象完全按照已知方式行动
  • 模拟类库:Mockito官网

    • 调用mock()方法创建模拟对象,并将模拟目标类型的class对象作为参数传进去。
    • 然后要把模拟对象需要的行为记录下来,通过 when方法表明要记录哪些方法的行为,然后用thenReturn方法指定期望结果。

参考: 面向开发的测试技术(一):Mock

既然使用模拟技术, 就要让模块的设计更易于测试, 也就是耦合更低, 之间的依赖更少, 一个正向循环呢

  1. Mockito Official Site 该框架有多种语言的实现 Java Python …
  2. Easy Mock Official Site

软件测试方法

以下是用于判断产品行为和性能的不同方法。黑盒子和白盒测试是两种基本方法。

黑盒测试

也称为功能或基于规范的测试,此方法侧重于输出。测试人员不关心内部机制。他们只检查软件是否符合预期。编码知识不是必需的,测试人员在用户界面级别工作。

白盒测试

此方法使用编码技术作为测试过程的一部分。当产品发生故障时,测试人员会根据需要深入到代码中以找到原因。软件开发人员自己这样做,因为他们确定产品应该如何工作。基于结构和玻璃盒测试是此方法的其他名称。

静态测试

测试人员检查软件的代码和文档,但不执行程序。在验证过程中,静态测试在产品开发的早期开始。

动态测试

软件使用各种输入执行,测试人员使用此方法将输出与预期行为进行比较。

GUI测试

测试GUI特性 - 文本格式,文本框,按钮,列表,布局,颜色,字体,字体大小等。GUI测试非常耗时,第三方公司经常承担这项任务而不是开发人员。

测试级别

这些对于识别软件开发生命周期的每个阶段中的弱点和重叠区域是必要的。

单元测试

开发人员测试代码的最基本部分,如类,接口和函数/过程。他们知道他们的代码应该如何响应,并可以根据输出进行调整。

  • 3A 原则:
    • Arrange: 初始化测试对象或者准备测试数据
    • Act : 调用被测方法
    • Assert: 断言

组件测试

其他名称是模块或程序测试。它与单元测试类似,但包含更高级别的集成。对软件模块进行缺陷测试,以验证其各自的功能。

集成测试

这可以在集成模块时识别错误。不同的集成测试是自下而上,自顶向下和功能增量。

The correct way to use integration tests in your build process

系统测试

使用此方法在不同环境中对项目的组件进行整体测试。它属于黑盒方法,是该过程中的最终测试之一。它确定系统是否能够满足业务和用户需求。

Alpha测试

内部员工在模拟或实际环境中在开发人员的站点测试软件。之后,开发人员纠正错误和其他问题。

Beta测试

也称为现场测试,客户端在实际条件下在自己的站点上测试产品。客户可以为一组最终用户提供通过预发布版或测试版测试软件的机会。然后将关于可能改进的反馈发送给开发人员。

验收测试

在黑盒测试的范围内,客户端测试软件以确定开发人员是否已根据所需规范创建程序。

测试类型

这些软件测试侧重于特定目标。

安装测试

软件测试工程师和配置管理器进行此测试以确保最终用户可以安装和运行该程序。它涵盖了安装文件,安装位置和管理权限等区域。

开发测试

这实现了一系列同步策略来检测和防止缺陷。它包括静态代码分析,同行代码审查,可追溯性和指标分析。目的是降低风险并节省成本。

可用性测试

通过此测试,用户体验受到关注。它衡量GUI的设计和易用性。该测试检查功能的准确性和效率以及测试对象的情绪反应。

完整性测试

这表明软件是否值得花时间和成本继续进一步测试。太多的缺陷和更激进的测试不遵循。

回归测试

当系统进行修改时,回归测试会监控意外行为。它指出了对模块或组件的不利影响。

wiki: regression testing

破坏性测试

测试人员输入异常条目并识别软件管理意外输入的能力。这向开发人员展示了程序在错误管理方面的稳健性。

恢复测试

当硬件或其他功能出现故障时,此测试显示软件恢复和继续运行的程度。

自动化测试

这会执行难以手动实现的功能。它使用特定的软件来运行测试并提供实际与预期结果的数据。

Aqua: The IDE for test automation

兼容性测试

软件必须在不同的计算环境中运行,因此这将检查与不同系统的兼容性。例如,该软件是否适用于各种操作系统和Web浏览器?

性能测试

这是一项深入测试,用于检查不同场景下的软件性能。收集有关响应性,稳定性,资源分配和速度的信息。此外,诸如体积,容量和尖峰测试等子测试在此过程中起作用。

安全测试

衡量软件保护用户安全的能力。这意味着授权功能,身份验证,机密性,完整性,可用性和不可否认性。

可访问性测试

这与可用性测试不同。这决定了包含学习和身体残疾的不同能力的用户可以使用该软件的程度。

国际化和本地化测试

结果显示软件如何适应不同的语言和区域需求。这包括为特定位置添加组件和翻译文本。

A/B测试

对照实验,也叫随机实验和 A/B测试 参考来源:知乎问题 | 相关三方平台

参考 ab-testing

A/B测试其实是一种“先验”的实验体系,属于预测型结论,与“后验”的归纳性结论差别巨大。A/B测试的目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量可信。

说句题外话,大量的大数据公司都在尝试通过“后验”结论进行未来行为预测,个人觉得然并卵,主要是因为数据不全、脏数据、随机事件、建模人为因素等等影响,方向无比正确,现实无比残酷

A/B测试的应用场景

  • A/B测试这种方法论的应用非常广泛,这里讨论在Web产品、移动产品、数字广告优化领域的应用。应用场景由小到大可以可以分为:
    • 元素/控件层面
    • 功能层面
    • 产品层面
    • 公司层面
元素/控件层面

灰度发布 和 A/B测试:重要页面的修改和流程上的调优,通过灰度发布到1%或者5%的用户,看其实际对用户的数据影响(访问时间增加、留存提高、下单率提高等),决定此修改到底是100%发布还是被砍掉 Google:

  • 每个月从上百个A/B测试中找到十几个有效方案,月营收提升2%左右,10亿美元的规模
  • 广告位左移一个像素带来X%的增收,左移两个像素带来Y%的亏损
  • 任何产品改动需要A/B测试才能上线 Facebook:
  • 6个月内所有版本完全线上灰度发布,通过不断进行用户流量分割的方式进行实验,获得无Bug口碑

灰度发布: 先把一部分线上的业务分流到新系统上, 然后看运营情况, 再判断是否全部上线

功能层面

无论是推荐算法还是定价策略 为了简单理解 一个价格包含这个几个因素:

1.价格区间:
用我最朴素的理解,人类是喜欢折扣的不理性动物:人们明显更乐意花45折买一个价值900块钱的东西而不是花67折买一个价值600块的东西,尽管东西一样,最终价格一样都是400块。
所以你看电商广告都是打折配合几个垫背的低价来卖。。。

2.价格精度:
在某些情况下,即使几分钱的价格变化对用户转化的影响是巨大的,比如一个东西原来卖400元,那改成399还是401可能对当下总营收的影响不明显,但是配合用户转化率的变化,营收的差异就天差地别了。

3.价格周期:
伴随着产品迭代、促销等等因素影响,什么时候降价是对自己最有利的策略,完全可以A/B测试来解决

产品层面

A/B测试在产品层面的应用主要是通过“灰度发布”来实现的。 就目前移动端的产品来说,iOS的应用商店审核期是个大大大坑,任何BUG打补丁还得再来一遍,也就意味着补丁的审核期内用户带着BUG使用,这个太致命了,用户的获取成本高的吓人,因为这个流失太不值得了,基于A/B测试的灰度发布更重要的不是优化,而是保护性发布,先通过小流量的实际用户测试,有BUG或者新版本体验不好,可以立即回滚到老版本,简单有效。

公司层面

A/B测试其实也是谷歌管理方法论,具体文章请参考:

相关问题

辛普森悖论(Simpson’s Paradox)亦有人译为辛普森诡论,为英国统计学家E.H.辛普森(E.H.Simpson)于1951年提出的悖论,即在某个条件下的两组数据,分别讨论时都会满足某种性质,可是一旦合并考虑,却可能导致相反的结论。
相关:辛普森悖论:诡异的男女比例

大多数的产品或功能上线前都会进行测试,实际上很多的测试行为并不科学,特别是很多定向的用户测试经常会有这个弊端,简单来说,如果新上线的一个功能,所有的研发工程师都说好,那是不是意味着所有的用户都觉得好?很多情况下是否定的。当然这个例子比较简单,实际上很多A/B测试方法并没有考虑到这个问题,以至于最后得出的结论跟实际情况差异巨大。 要解决这个问题,对采样、聚类、流量分割等要求非常的高,这也是为什么A/B测试工具不能像很多统计工具一样,埋个点看数据,再根据数据反推业务逻辑,而是要充分与业务结合,从一开始就应该考虑业务策略,让用户去选择适合其口味的产品。 现在的创业者面临着前所未有的竞争压力,好的想法与用户接受的想法有着各种不可逾越的鸿沟。特别是伴随着激烈的竞争,谁能领先一步可能就变成了赢者通吃的局面。

相关专栏: [A/B]那些年,我们追过的AB Testing (一) | [A/B]那些年,我们追过的AB Testing (二)从“People you may know”到growth hacking


TDD 测试驱动开发

Java程序员修炼之道 测试驱动开发章节

p276.jpg

辩证看待QA人员和Unittest

TDD带来的好处

  • 更清晰的代码 只写需要的代码
  • 更好的设计 有些开发人员管TDD叫测试驱动的设计
  • 更出色的灵活性 TDD会鼓励按接口编码
  • 更快速的反馈 不会直到系统上线才知道bug的存在

红 - 绿 - 重构循环

  • 先编写不能通过测试的红色代码,就是一个简单的思路编写

  • 然后修改,让测试通过,然后完善设计,进行重构编写,又进入红绿阶段,然后再重构

  • 重构是为了通过测试你写的快速实现,重构减轻自己和别人的技术债务(临时凑合出来的设计或代码将来会付出更多的工作)

    • 有了通过测试, 就可以放心的重构,应该实现的业务逻辑就不可能会被忽视
  • 多个测试用例 为了覆盖更多情况

    • 按照TDD风格,就应该为之前编写的测试用例,进行多路径全覆盖,这个测试依然是红绿重构循环。

深入思考 红 绿 重构循环

  • 失败测试(红)
    • 一些开发人员喜欢编写编译失败的测试,喜欢等到绿色步骤才提供实现代码
    • 也有一些开发人员喜欢先把测试调用的方法签名写出来,这样虽然编译能通过,但还是失败
  • 通过测试
    • 这一步应该尽量少写代码,只要能测试通过即可。
  • 重构
    • 许多地方需要重构,一般是: 去掉硬编码的变量,
    • 或把大方法拆分,对于面向对象来说应该遵循 SOLID 原则 解释SOLID原则
    • 可以把通用的设置和拆卸代码提取出来。可以重命名测试,以便于更准确地反应他的意图,根据静态分析工具例如CheckStyle FindBugs p283.jpg
  • 应该尽可能的遵守单个测试循环的开发模型,不要同时开多个测试循环,一堆红色

什么是TDD

百度百科词条, 简单来讲就是红绿循环, 红 绿 红…
TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。
TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。

  • 个人理解:引用Mockito框架中的 “when thenReturn” and “when thenThrow“ 由预定输入,运行方法或模块后,需要返回预定的数据或者抛出预定的异常,这就是已经写好了测试
  • 然后根据测试原型,去思考真正的功能代码的实现,当代码实现后,能够通过之前写的测试就代表着一个方法或模块的开发成功,然后开发下一个
  • 要想做到这样的地步,首先基础的环境要思考好,耦合的问题要明确,确定公共模块之后,再一个个的TDD进行开发。一个很好的思想,不用担心你之后的改动会让代码变得丑陋不堪

优点:在任意一个开发节点都可以拿出一个可以使用,含少量bug并具一定功能和能够发布的产品。
缺点:增加代码量。测试代码是系统代码的两倍或更多,但是同时节省了调试程序及挑错时间。

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数abs(),我们可以编写出以下几个测试用例:
输入正数,比如1、1.2、0.99,期待返回值与输入相同;
输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;
输入0,期待返回0;
输入非数值类型,比如None、[]、{},期待抛出TypeError。
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。

TDD实践

Java测试驱动开发(TDD)之《井字游戏》

BDD

行为驱动开发 behavior-driven development 作为TDD的一个变种

cucumber about bdd

它的重点是描述程序的行为, 而不是一组需要通过的单元测试