AI人工智能与软件测试

测试并不能改进产品质量

今天看到了有人给出了这样的观点,那就是测试并不能改进产品质量,觉得挺有意思的,所以把作者的原文翻译一下,希望对大家有所裨益。

在世界上有一种天经地义的看法,那就是在软件开发过程的早期预防问题会比在测试后期发现问题带来更高质量的产品,但那不是真的。

这是不真实的,但不是因为大多数人可能首先想到的原因。问题不在于尽早解决问题是个坏主意。这通常是一个非常好的主意。

问题是,测试自身不会导致更高质量的产品,在所有。问题预防和测试是不同的追求。测试不能预防问题,测试不能改进产品。

对我而已——一名教师和熟练测试的倡导者——这可能看起来很疯狂,但这是真的。称体重不会让你减肥。验血不会让你更健康。学校的标准化考试不会让孩子更聪明,当然也不会提高教育质量。

测试可以做的是通过质疑产品以对其进行评估来提高我们对可能摆在我们面前的事物的理解和意识。测试——通过体验、探索和试验了解产品来评估产品的过程——帮助我们意识到可能需要解决的问题。

在日常生活中,浴室秤上的某个特定读数可能会促使我们更加谨慎地进食,或者进行更多的锻炼。验血可能会促使医生开抗疟疾药物。这些标准化的学校考试可能会建议改变课程、教育资金或教师培训。但在有人采取行动之前,测试只会提高对现状的认识,而不是情况本身。

在软件开发中,除非有人解决测试帮助我们发现的问题,否则改进不会发生。当然,如果问题没有被发现,改进的可能性就会大大降低——这就是测试如此重要的原因。测试帮助我们了解我们拥有的产品,因此我们可以判断是否是我们想要的产品。在需要改进的地方,测试揭示了改进的必要性。

有些人认为测试要求我们操作产品,这是软件开发流程的一部分。确实这是是一种非常重要的测试,但也只是产品测试的一种方式而已。

将产品更广泛地理解为某人生产的东西会很有帮助。这意味着我们可以有单元,模块,原型等多种手段的测试。

尽管我们通常将其称为审查,但我们确实可以在产品开发之前就进行全方位的思考,在这种情况下,产品其实就是之前所有的讨论,设计和思考的产出物,测试其实在产品的研发的初期就开始了。

测试的结果是通过这些活动进行的评估和学习。这种学习可以应用于产品——但它是响应测试而发生的事情,而不是测试本身,这是可以改进产品。

正如测试不能改进产品一样,测试也不能防止问题。作为测试人员,我们有一个持久的信念,即我们被要求测试的任何东西都已经存在问题。也就是说,产品中的问题在我们遇到之前就已经存在。

那么,如果测试不能预防问题,那又有什么用呢?测试可以帮助我们意识到存在的问题,然后人们可以进行更改以防止这些问题进一步发展。

在开发早期尝试预防问题是一个好主意。如果尝试能够成功,我们就更有可能开发出高质量的产品。尽管如此,即使是高度规范的开发过程也可能会出现问题。至少有两种方法可以确定是否发生了这种情况。

第一种方法是全程测试产品。通过与一系列客户互动并了解他们所做的事情,确定我们开发的东西就是客户想要的。通过检查和讨论,测试那些最初模糊然后却越来越清晰的设计。

另一种方式是在代码开发的初级就做单元测试,然后在后续的构建步骤里进行集成测试,code review,静态代码扫描和自动化测试,从而找出组件自身的问题或者是配置的问题。这些形式的测试通常不是很深入——这是一件好事,因为深度测试可能需要时间、精力和准备,这可能会对开发人员造成更多负担。

在开发人员测试的同时,测试人员专注于并执行深度测试。深度测试的目标是测试少见的、隐藏的、微妙的、偶尔发生的、紧急的错误,这些错误可以通过手工测试或者探索性测试来发现。

如果您的问题预防和问题缓解策略是成功的,并且你的代码和产品是可测试的,那你不太可能在测试后期遇到一些显而易见的低级问题。如果您不需要测试和报告这些问题,那么后续的深度测试会相对更快、更容易。

如果您的问题预防和问题缓解策略不成功,全面和深入测试是找出问题的一种方法。您发现的问题可以解决;可以改进产品,并且可以在产品交付之前防止业务和客户出现问题。

当人们面临损失、伤害、不良情绪或价值降低的风险时,最好在为时已晚之前意识到问题,这就是测试的终极意义之所在。

测试本身既不能防止问题也不能改进产品。但是测试确实可以预测需要预防的问题,并且测试可以照亮产品需要改进的地方。

因为全文大部分是机翻,所以行文肯定有不流畅的地方,另外加上作者本身的笔法比较曲折,有一些点机器翻译可能难以驾驭,因此全文可能看起来观点比较分散,没有强一致性,不过大家可以简单的意会,忽略那些遣词造句里的机器感。

总而言之作者的观点其实并不离经叛道,而且还是有一定的道理的,比如癌症筛查并不能治疗癌症,不过如果你尽可能早的可以通过筛查确定自身的风险,提前治疗和预防,那么癌症很有可能将不会发生。测试也是如此,测的再多开发和产品不去修改,那么产品本身的质量也不会得到改进,但是如果我们频繁的提前的进行测试,那么我们就可以尽早的暴露问题,我们的开发和产品也就会有足够的时间去进行修复工作。这也是为什么我们总强调 bug 生命周期的原因,测试并不只是简单的提 bug,而是要尽早提 bug,并保证 bug 得到及时修复并得到验证。

原文地址:Testing Doesn’t Improve the Product (linkedin.com)

使用重试模式来提升自动化测试的稳定性

看到一篇非常好的关于在自动化测试中引入重试模式的文章,https://www.thegreenreport.blog/articles/enhancing-automation-reliability-with-retry-patterns/enhancing-automation-reliability-with-retry-patterns.html 忍不住跟大家分享一下。

自动化测试,特别是 ui 自动化的稳定性提升是非常困难的,出错了就重试这个简单的策略可以帮助我们处理一些难以预料的异常情景,从而提升测试的整体稳定性。

这篇文章的作者就分享了几种重试的设计模式,通俗易懂而且相当实用。

动态重试

async function dynamicRetry(actions, maxRetries) {
  let retryCount = 0;

  async function retryAction() {
    try {
      return await actions();
    } catch (error) {
      if (retryCount < maxRetries) {
        retryCount++;
        return retryAction();
      } else {
        throw error;
      }
    }
  }
  return await retryAction();
}

it("Testing the dynamic retry pattern", async () => {
  await demoPage.navigateToDemoPageTextBoxes();
  await browser.pause(1000);
  await dynamicRetry(async () => {
    await demoPage.fillFullName("John Doe");
    await demoPage.fillEmail("test@gmail.com");
    await demoPage.fillCurrentAddress("test address");
    await demoPage.clickFakeButton();
  }, 3);
});

该模式的核心是设置一个最大的重试次数,每次重试过后次数加一,直到达到阈值。

轮询重试

另一种常见的重试模式是使用超时和轮询。这种方法会重复检查一组操作是否成功完成或者是否达到了超时时间。

async function pollRetry(actions, timeout, pollInterval) {
  const startTime = Date.now();
  let lastError;

  while (Date.now() - startTime < timeout) {
    try {
      await actions();
      return;
    } catch (error) {
      lastError = error;
      await sleep(pollInterval);
    }
  }

  throw lastError;
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

it("Testing the poll retry pattern", async () => {
  await demoPage.navigateToDemoPageTextBoxes();
  await browser.pause(1000);
  await pollRetry(
    async () => {
      await demoPage.fillFullName("John Doe");
      await demoPage.fillEmail("test@gmail.com");
      await demoPage.fillCurrentAddress("test address");
      await demoPage.clickFakeButton();
    },
    20000,
    5000
  );
});

这个模式的核心是设置一个总的超时时间,如果在这个时间段内发生了异常,那么就每隔一段时间重试一下。

什么是向量数据库

对于向量数据库有疑问吗?以下是对这个数据库的简要解释。

什么是向量?

向量是一组数字的数组,表示点在空间中沿多个维度的位置。例如,向量{12, 13, 19, 8, 9}表示一个点在 5 个维度上的位置。向量可以几何地表示数据,使得数据点之间可以进行数学比较。

向量与嵌入有什么关系?

在机器学习中,嵌入是表示数据点(如单词、句子、图像等)的向量。嵌入将数据的语义含义编码为向量形式。它们由经过训练的神经网络生成,这些网络将数据点映射到有效的向量表示。这些嵌入将相关概念放置在向量空间中靠近的位置。

向量数据库如何工作?

向量数据库存储了各种数据点(如文档、图像、产品等)的嵌入,并将这些嵌入映射回其原始数据。数据库可以基于向量的相似性进行快速的相似性搜索。查询嵌入可以与存储的嵌入进行比较,最相似的向量可以几乎即时地被检索出来。这为语义搜索、推荐和其他人工智能应用提供了支持。

向量数据库在人工智能中的应用是什么?

向量数据库具有几个关键的人工智能应用:

  • 语义搜索 - 基于含义而不仅仅是关键词来检索信息
  • 推荐 - 提供与用户兴趣最相关的内容
  • 大型语言模型 - 理解上下文中的单词,用于生成和摘要
  • 异常检测 - 识别与正常情况相比的异常数据点
  • 图像分类 - 基于视觉属性对图像进行分类

通过促进快速的相似性比较,向量数据库使得机器学习模型能够在数据之间建立联系。这支持了使用原始数据无法实现的高级人工智能功能。向量空间充当算法构建理解的一种"记忆"。

移动测试指南

Guide to Mobile Testing

移动应用已经风靡全球,在移动互联网如此普及的今天,移动测试的重要性不言而喻。个人认为移动应用现在是大部分互联网公司的主要业态,在竞争激烈的现时,移动应用的质量可能会影响某个产品的成败与存亡,在去测试化思潮如此普遍的今天,移动端应用应该是最不可能砍掉测试人员的。因为

  • 移动应用的质量是第一大事
  • 竞争越激烈,好的质量越重要
  • 测试移动应用的效率目前还是偏低,用高薪的开发人员去兼任测试职能有点得不偿失
  • 移动端更新成本相对较高,测试充分再发出去才能尽可能地规避更新应用进行 bugfix 的风险

诚然,有些公司在推进去测试化,但那些公司可能是

  • 所在行业垄断性质明显,没有其他公司可以进行正面竞争,比如微软
  • 公司和用户对线上问题忍耐度相对较高,应用形态相对单纯,试错成本相对较低,比如之前的头条
  • 行业已过高速增长期但存量竞争激烈,产品基本处于维护阶段,不需要投入太多的开发和测试资源,比如腾讯的某些产品

从这里开始就是翻译了,原文地址在:https://medium.com/@iamfaisalkhatri/guide-to-mobile-testing-d0dd2d9b59f1

所以大部分情况下,移动端的测试以及质量提升是需要专职测试人员进行负责和统筹的,所以不必焦虑。相反,了解一些基本的移动端测试知识是一件较为必要的事情。

质量是关键

我们需要检查所有内容以及所有可能的排列和组合,以免出现任何 bug。由于移动设备也存储了最终用户的个人数据,因此有必要对安全性和数据完整性进行检查。

测试应用程序的性能同样重要,因为如今人们对应用程序的速度更感兴趣。如果功能能用,但应用响应时间过长,可能无法吸引用户。因此,应用程序的性能测试也是需要考虑的重要因素。

谈到测试移动应用程序,我认为我们应该首先弄清楚测试策略,因为它可以帮助我们分解测试阶段并进行高质量的测试,并帮助我们避免漏测一些重要内容。

定义测试策略

首先我们需要弄清楚移动应用的类型

  • 原生应用:离线可以直接用安装包进行安装,一般情况下可以在应用商店下载
  • 移动 web 应用:比如 h5 应用,基本是用 html+css+js 进行开发的,可能长得跟原生应用很像,但基本上不是一回事
  • 混合应用:既有原生也有移动 web 的应用,一般情况下对开发人员比较友好,毕竟 web 应用比原生应用开发要容易一点点

我们以混合应用为例在说明一下如何定义我们的测试策略,在进入细节之前,我们先来了解一下测试类型的相关知识。

测试类型

理想情况下,虑到混合应用程序,我认为应该考虑以下测试类型

  1. 功能测试。
  2. 性能测试
  3. 安全测试
  4. 可用性测试
  5. UI/UX 测试

%E7%A7%BB%E5%8A%A8%E6%B5%8B%E8%AF%95%E6%8C%87%E5%8D%97%2031ddadb1681b4287803ef98d6ca78960/Untitled.png

何时以及如何开始测试

由于我们处于当今软件世界虔诚地遵循敏捷的时代,因此最好尽早开始测试。

测试应该在软件开发生命周期的每个阶段进行,而不仅仅是在功能完全开发时进行。

话虽如此,请始终确保开发人员正在编写单元测试。还应涵盖集成和服务层测试。只写测试没有帮助,代码覆盖率报告应该显示单元测试覆盖率至少大于 80%,如果有可能的话,可以逐渐增加到 100%。有一条流水线可以帮助我们轻松监控生命周期并在每个阶段采取纠正措施,这很好。因此,除非构建是绿色的,否则继续进行测试并尽快进行所需的修复是不好的。

测试计划

有一个测试计划是很好的,因为它会更容易检查所有的测试活动,所以我们不会遗漏任何东西,顺利地执行测试并提供高质量的输出。

第一个也是最重要的情况是用户是否能够使用 PlayStore/App Store 成功安装应用程序。

Selenium Manager使用指南

Selenium 社区最近发布了 Selenium Manager 工具,主要是解决每隔一段时间就要重新去下载 driver 的痛点。当然了,这也是我痛点。因为 chrome 浏览器会自动更新的关系,之前我都是关掉自动更新从而回避去下载新 driver 的问题,不过因为用户的版本大多都是较新的,所以测试浏览器每隔一段时间还是要更新一下才比较好。我自己试用了一下,感觉还是很不错的,非常有意思的一个点是 sm 竟然是 rust 开发的,在我的印象里这种对性能和稳定性要求不是特别高的命令行用 go 开发和维护的话效率可能会更高一点。下面是官方 blog 的翻译以及我自己的一点点体验。

大多数人使用 Selenium 的第一次经验都会出现这样的错误消息:

java.lang.IllegalStateException: The path to the driver executable must be set by the webdriver.chrome.driver system property; for more information, see https://chromedriver.chromium.org/. The latest version can be downloaded from https://chromedriver.chromium.org/downloads

然后他们不得不在网上搜索有关如何处理他们下载的驱动程序的说明。

Selenium:现在已经内置了驱动程序!

Selenium 项目希望改善用户体验,其中一项首要任务是帮助所有用户简化他们设置环境的方式。多年来,配置浏览器驱动程序一直是用户需要执行的任务,以便运行 Selenium。

设置一次浏览器驱动程序并不那么复杂,但随着浏览器发布周期缩短,现在每 4-6 周就有一个新的 Chrome/Firefox/Edge 版本,使得保持浏览器驱动程序与浏览器版本同步的任务变得不那么容易了。

Selenium Manager 是一个新的工具,可帮助轻松获得运行 Selenium 所需的工作环境。如果 Chrome、Firefox 或 Edge 不在 PATH 中,Selenium Manager Beta 1 将配置它们的浏览器驱动程序。

确认:代码覆盖率是一个无用的管理指标

看标题就知道这篇文章很有意思,作为一个之前专注过做代码覆盖率平台的人,这个观点一直模模糊糊是存在于我的心里的,不过从来没有这么直截了当的说出来,这篇文章的作者有理有据,值得一看。原文在这里: https://drpicox.medium.com/confirmed-code-coverage-is-a-useless-management-metric-35afa05e8549,我只做了一些翻译。

有一种强烈的信念,即代码覆盖率是衡量软件产品质量的重要指标,这个信念多年来一直被技术领导者们毫无疑问地共享。从表面上看,这个理论似乎很有道理:测试越彻底,代码覆盖率越高,因此我们的软件应该更加健壮和无错误。这个观念已经深深植入我们的思维中。但是,如果我能证明代码覆盖率从根本上是错误的呢?如果我能向你展示一个如此简单的想法,让你对此毫无疑问?所以,请做好准备,做好心理准备。

鉴于本文仅展示了哪些指标不适用于管理(尽管对开发人员非常有用),但并未说明应该遵循哪些指标,因此我最近写了一篇后续文章,解释了应该使用哪四个基本指标以及为什么要这样做,这些观点都有科学依据

The Code Coverage 代码覆盖率

代码覆盖率,简单来说,是衡量你的代码有多少被测试所“触及”或“覆盖”的一种度量。我们假设在我们的产品中有测试,并且我们至少在每次发布之前运行这些测试。当这些测试执行时,它们会对产品进行操作,从而使代码执行。很快,我们意识到如果我们追踪哪些代码被测试执行,我们就可以开始衡量有多少代码被执行了。我们将执行的代码与产品中的总代码量的比例称为“代码覆盖率”。

code coverage = executed code by tests / size of the code

这是一个非常简单的度量标准。如果我们有 100 行代码,但测试只执行了其中的 75 行,那么我们的代码覆盖率为 75%。

很快我们意识到了更重要的事情。如果代码覆盖率不是 100%,那就意味着我们的测试没有执行到某些代码,换句话说:我们有未经测试的代码!

因此,拥有未经测试的代码是危险的,因为它可能包含错误。此外,它还可能包含业务关键功能,如果我们触碰到该代码,我们可能会丧失这些功能。

所以,拥有高代码覆盖率是必须的。

代码覆盖率的谬论

但是,现在我们面临一个谬论:我们知道揭示代码意味着我们的测试遗漏了重要的情况,但反过来并不成立。

例如,在之前的例子中,我们的代码覆盖率为 75%。换句话说,这个指标表示有 25%的代码行没有被任何测试执行过,这明确指出了一个风险区域。我们可以确定地说,这 25%的代码库没有经过任何测试验证,因此可能成为问题和维护困难的滋生地。

然而,这就是我们冒险陷入谬误的时候:虽然我们可以自信地说未经测试的代码隐藏了潜在的错误和对未来发展的阻碍,但我们可能相信相反的情况是真实的。我们可能相信代码覆盖意味着它有更少的错误和更少的维护问题。但是,这只是一个直觉,甚至可能看起来很合乎逻辑,但事实证明这并不正确。

事实是,我们可以实现 100%的代码覆盖率,但代码仍然可能存在大量的错误,并且难以维护。

一个基本的例子

想象一个简单的函数,它计算两个数字的和:

function addition(a, b) {
  return a + b;
}

哪个是能够覆盖 100%的最简单的测试?只需添加一个附加项即可使所有代码执行:

test('the addition function', () => {
  addition(3, 4);
});

这个测试覆盖了 100%的代码。然而,它是无用的。为什么呢?如果我们将加法的实现改为这样一个:

function addition(a, b) {
  return a - b;
}

测试仍然通过!