AI人工智能与软件测试

使用openAI来分析自动化测试报告的错误信息

今天看到有人用 ai 去分析自动化测试报告里的错误,代码和概念都很简单,也很有意思,忍不住翻译了一下,供大家参考。

原文地址https://labs.pineview.io/using-openai-platform-to-analyse-automated-test-failures

引言

当涉及到人工智能,尤其是 OpenAI 平台时,关于它将如何影响一切的内容并不缺乏。因此乍一看,本文可能似乎是另一篇过于热情和乐观的标题党文章,告诉你应该加入人工智能的行列,否则就会被落下。

顺便说一下,本文并非由人工智能撰写。我只是使用我最喜欢的文本编辑器应用程序,它以西方文学中最令人激动的小说之一的名字命名——尤利西斯。除了一些基本的自动完成功能外,没有来自外部的干扰(或推理)。本文没有人工智能生成的废话,尽管我不能保证文章完全没有废话。

但无论你对人工智能生成内容持何种立场,作为软件专业人士,我认为我们都可以达成共识:在自动化软件测试方面,调试和调查测试失败总是很繁琐。因此,我认为这可能是一个可以引入一些人工智能辅助的好领域,因为我们只是在扩展机器已经完成的工作。在这里,不存在冒充人类或“让人们误以为他们正在与真人互动”的风险,这正如哲学家丹尼尔·丹尼特在他最近在《大西洋月刊》上发表的文章中所提到的一个真正的文明风险。

什么是端到端测试?

如果你对端到端测试还不熟悉,它是一种通过模拟真实用户的操作来测试整个应用程序的自动化软件测试类型。

Nightwatch.js 是一个开源库,用于编写和执行网站和 Web 应用程序的自动化端到端测试。它于 2014 年发布,2021 年被转移到 BrowserStack 的开源计划办公室,目前正在进行开发。Nightwatch.js 是用 Node.js 编写的,它支持所有主要的 Web 浏览器,并且还可以在移动设备上运行测试。

本教程将介绍如何开发一个 Nightwatch.js 插件,将测试失败和相关错误发送到与 OpenAI 平台集成的服务,以分析错误并获得一些可操作的反馈。默认情况下,Nightwatch 的最新版本已经对测试失败提供了相当好的反馈,并提供了一定程度的可操作反馈,因此我们将尝试使用 GPT-4 模型扩展其功能,以在输出消息中增加一些亮点,提供稍微更好的上下文,并学习如何开发结合人工智能辅助的服务。

为什么选择 Nightwatch?

诚然,目前市场上还有一些其他备受炒作和流行的测试工具,但实际上 Nightwatch 是我们在 2014 年在 Pineview 创建的项目,现在正在 BrowserStack 的开源计划办公室进行开发。我也是那个团队的一员,Nightwatch 仍然是我在所有其他项目中用于测试的最喜欢的工具,当然。

此外,Nightwatch 作为一个库已经存在了相当长的时间,在这些年里享有不同程度的受欢迎程度。有大量的可用于机器学习模型训练的数据,因此 GPT-4 在编写 Nightwatch 测试和解释结果方面具有相当好的能力,这意味着我们已经有了一个强大的基础,可以构建一个辅助人工智能来解释我们的测试失败,并可能与我们对抗。

步骤 1 - 创建错误分析服务

我们的小练习主要由两个部分组成,都相对简单:

构建调用 OpenAI 服务的后端服务 编写 Nightwatch.js 插件,接收实际的测试失败并将其发送到后端服务进行分析 我们将从第 1 部分开始 - 构建错误分析服务。在当今时代,构建与人工智能相关的任何东西可能听起来非常奢侈和光鲜,但实际上这只是一个非常基本的任务,并没有太多特别之处。

测试同学绩效不好怎么办

今天是 2024 年的 3 月 8 日,祝所有的测试女神们节日快乐先。

昨天跟一位同学聊天,发现他去年的年终绩效不是很好,最终可能影响了整体收入,忽然想到是时候聊一下测试同学的绩效问题了。

作为一个有多年测试管理经验的老人家,之前也经历过非常多的绩效考核和 kpi/okr 制定,经历的多了自然就会发现一些套路,一般来说测试同学的绩效不好大概是因为如下几个方面。

质量输出效果很差

这点容易理解,可以分解为两个方面的输出比较不尽如人意。

  • 结果指标。比如线上问题多,漏测率高等等;
  • 过程指标。比如测试过程中发现不了什么问题等等;

质量输出一般都是有指标来衡量的,要提升的话其实也比较直接:想办法提升核心指标或者加入更多维度的指标来对冲核心指标带来的不利影响。 这点的原理其实有点反直觉,那就是数据其实是会说谎的,片面的指标其实不能真实的反映出现实的问题,举个例子,我的自动化测试用例覆盖率是 100%,这个指标看起来很厉害,但现实可能是我只有 1 个用例,而且完全被覆盖了。所以如果指标没有多维度多角度的话,其实想提升还是有办法的,比如漏测率指标可以想办法提高分母,覆盖率指标可以想办法减小分母之类,只要指标比较单薄,那么操作的空间还是有的。另外就是加入更多的对自己有利的指标,这样也能获得一个不错的观感,毕竟打绩效很多时候都是非常主观的事情。

输出效能不够理想

我们现在经常说内卷,我觉得可以再加个外卷,从不原教旨主义的基础上扩展一下卷这个词的适用范围。

  • 外卷,向外去卷。卷竞品,卷价格,卷服务之类的,比较好理解的一个例子就是《唐伯虎点秋香》里面周星驰华府求职卖惨那一段,没错,就是小强这个梗梦开始的地方。
  • 内卷,卷死自己。就是要想马儿跑得快,又要马儿不吃草,别人加班到 9 点,我就要加到 10 点,别人 1 周测 1 个需求,我 1 天测 1 个,并美其名曰这是我效能突出。

所以输出效能不理想实际上就是你的 report 对象觉得你不够卷自己,单位时间内做的事情不够多,效率不够高。

这种情况下,一般来说也是有指标加持的,提升的方式跟上面思路是一样的,想办法提升核心能效指标或者加入更多维度的对自己有利的能效指标。

另外新技术和新方法的引入也能有效的缓解这个问题。说白了就是从上到下都焦虑,引入新的概念有点像是吃抗焦虑药,如果能持续吃下去的话,那么问题在很长一段时间是可以被缓解的。

测试不重要

团队或者你的 report 对象觉得测试不重要,因为绩效是很主观的东西,所以你的绩效一定不会太好。

这个想要破局就非常困难了,毕竟没有统一的方法,不过既然有测试这个岗位存在,那就证明团队里大多数人是疏于测试的。加上现在微服务化之后,业务逻辑被拆的七零八落,很难有人能够从全局上去把握整体的业务逻辑了。所以建议还是要更贴近业务,做最熟悉业务的那个人,毕竟测试不重要但是业务很重要,用黑话说就是把业务当抓手,更团队对齐颗粒度

离业务太远或者业务不行了

核心就是你的 report 对象觉得你对业务没有贡献,这点经常出现在测试开发和一些支撑型的测试人员身上,而且特别容易发生在业务处于平台期或者下降期的团队上。道理很简单,业务都不行了,盘子都变小了,那么就没必要维持那么大的团队规模了,不做业务或者离业务远的同学,不管开发还是测试也好,绩效都不会好。

这点其实没有非常好的应对方法,要么换个好点的业务团队,要么做新业务领域的探索和尝试,但总是说的容易,做起来难。

跟上级关系不好

这里分两种情况,一种是业务还行,但就是跟上级不对付,这证明有调整空间,可以参考第三点,先让自己变得重要起来,然后求同存异,希望有关系有所改善;第二种就是业务不行了,而且跟上级相处的不是很融洽,这时候就好好好考虑自己的未来了。

总结

绩效不好首先不要焦虑吧,人生总是起起伏伏的过程,不可能一帆风顺。前几天看罗伯特 艾格的自传,发现这位迪士尼 ceo 大佬的人生也不是到处开挂爽文男主,在他最困难的时候他也焦虑到不能控制他自己(字面意义上的),不过他最终走了出来,药方就是:工作只是生活的一部分, 别看的太重,但又不要不屑一顾,毕竟凭自己的能力自食其力的人都应该获得尊重。

单元测试用例该如何设计

最近一些大公司在进行去测试化的操作,这一切的根源大概可以从几年前微软一刀切砍掉所有内部正式的测试人员开始说起,当时微软内部的测试工程师有一部分转职成了开发工程师,他们的职能中有很大一部分的职责是教会普通开发人员如何进行测试。我们都知道开发人员进行的测试一般以单元测试为主,假如有一天你所在的组织需要你转变成一名测试方面的教练,除了自动化测试之外还需要去推广单元测试,那么你该如何去定义单元测试用例的设计方法论呢?这里给大家一些思路,看看简单的单元测试用例究竟该如何设计。

一个方法可以有任意数量的有效测试用例;它最终取决于方法的结构。有两种简单的方式可以帮助我们设计单元测试用例。

  • 参数方法
  • 执行路径方法

我将通过提供真实的代码来进行演示。所有代码片段都将用 C# 编写,断言将使用我最喜欢的单元测试包 Fluent Assertions。

我们将为以下方法提供测试用例:

public static bool ContainsNamelessItems(this List<Item> items)
{
  return items.Any(item => item.Name.IsNullOrEmpty())
}

此方法将项目集合作为参数。它遍历项目列表,并针对每个项目Item检查其 name 属性是否为空。如果 name 存在且不为空,我们返回True,否则我们返回False

使用参数方法创建测试用例

这种方式主要考虑的是入参可以传递哪些值。

查看该方法的参数 ContainsNamelessItems,我们有一个 List名为 items. 此参数可能有几个可能的值:

  • items 是空的
  • items 至少包含 1 个 Item 具有 Name 未定义的属性
  • items 不包含具有未定义 Name 属性的项目
  • items 是 null

这些可能的值中的每一个都可以作为单独的用例存在。

以下是一些可能的测试用例和断言:

1,当List<Item>为空时,我们期望返回值是False因为其的List<Item>无 name 属性。

public void WhenItemsIsEmpty_ReturnFalse()
{
  var items = new List<Item>();

  var result = items.ContainsNamelessItems();

  result.Should()
    .BeFalse("because an empty collection cannot contain nameless items");
}

2,当List<Item>包含至少 1 项没有 name 属性的Item时,我们期望返回值是True

public void WhenItemsContainsANamelessItem_ReturnTrue()
{
  var items = new List<Item>
  {
    { new Item { Name = "Item1" },
    { new Item { Name = string.Empty } // nameless item
  };

  var result = items.ContainsNamelessItems();

  result.Should()
    .BeTrue("because there is a nameless item in the collection");
}

3,当List<Item>不包含任何没有 name 属性的项目时,我们期望返回值是False,因为所有项目都有 name。

Selenium Manager可以用起来了

前几天随手写了几个 headless 的 selenium 爬虫脚本,运行的时候发现本地的 chromedriver 竟然不需要更新,一时间有点没反应过来,毕竟 selenium 有个痛点就是chrome 浏览器自动升级之后需要下载新的 chromedrier, 否则之前的脚本将会报错。当然了,之前也有一些规避的方式,比如

这些方法其实都挺好,都能解决核心问题,特别是 python 的 webdriver-manager,几行代码就可以保持 driver 永远自动更新,举个例子

# selenium 4
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))

这次不用更新 driver 是因为使用了官方推出的selenium manager,之前没留意,不过真到用的时候发现还是比较方便的。对我来说 selenium manager 最方便的就是初始化环境的功能,比如

  • 自动安装浏览器
  • 自动安装 driver
  • 支持多架构多系统
  • 可以配置代理,这点很重要
  • 自动管理浏览器和 driver,其实就是把浏览器和 driver 放在了系统 PATH 里

如果我有一个脚本需要在 windows 和 macos 的最新版本 chrome 上跑,那么环境初始化就非常容易了,只需要下面的命令

秒会selenium grid

今天在看 selenium grid 文档的时候,发现 selenium grid4 的设计还是不错的,想顺手体验一下,于是就发现了docker-selenium项目,可以快速的设置好 selenium grid 环境,非常简单方便。

然而后面准备用 python 去写个简单例子的时候,发现很难找到 python 代码的例子,好不容易找到 1 个却发现跑不起来,于是简单的看了下源码,找到了正确的打开方式,这里简单分享一下。

selenium grid 的使用场景

在我看来 grid 的使用场景有两个

  • 在不同的浏览器上并行跑用例,这比挨个在不同浏览器上跑要省不少时间
  • 启动多个节点在同一个浏览器上并行跑用例,同样也是节约了执行时间

快速安装好 selenium grid 环境

Selenium grid 有多种模式,比如 Standalone, Hub and Node,对于初次体验来说无脑用 Standalone 是不会有问题的。

传统的方式是使用 java 来运行 jar 包安装,不过 docker selenium 提供了更简单的方式,直接用 docker 跑镜像就好了。 比如下面的命令就启动了 1 个 firefox 的远程节点。

docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-firefox:4.11.0-20230801

这里暴露了 2 个端口

  • 4444: hub 的端口,直接访问可以看到所有节点的信息
  • 7900: vnc 的端口,访问 http://localhost:7900/?autoconnect=1&resize=scale&password=secret 就可以看到远程节点上的浏览器运行情况,非常方便了

连接远程节点进行测试

代码很简单,我的运行环境是

如何实现线程安全的内存缓存

这两天正好看到一个用 go 实现的线程安全的内存缓存,实现代码非常简洁高效,不卖弄不烧脑,非常值得初学者拿来学习。

项目地址

项目地址在https://github.com/muesli/cache2go,目前已经有 1.8k 的 star。

如何使用

package main

import (
	"github.com/muesli/cache2go"
	"fmt"
	"time"
)

// Keys & values in cache2go can be of arbitrary types, e.g. a struct.
type myStruct struct {
	text     string
	moreData []byte
}

func main() {
	// Accessing a new cache table for the first time will create it.
	cache :=

	// We will put a new item in the cache. It will expire after
	// not being accessed via Value(key) for more than 5 seconds.
	val := myStruct{"This is a test!", []byte{}}
	cache.Add("someKey", 5*time.Second, &val)

	// Let's retrieve the item from the cache.
	res, err := cache.Value("someKey")
	if err == nil {
		fmt.Println("Found value in cache:", res.Data().(*myStruct).text)
	} else {
		fmt.Println("Error retrieving value from cache:", err)
	}

	// Wait for the item to expire in cache.
	time.Sleep(6 * time.Second)
	res, err = cache.Value("someKey")
	if err != nil {
		fmt.Println("Item is not cached (anymore).")
	}

	// Add another item that never expires.
	cache.Add("someKey", 0, &val)

	// cache2go supports a few handy callbacks and loading mechanisms.
	cache.SetAboutToDeleteItemCallback(func(e *cache2go.CacheItem) {
		fmt.Println("Deleting:", e.Key(), e.Data().(*myStruct).text, e.CreatedOn())
	})

	// Remove the item from the cache.
	cache.Delete("someKey")

	// And wipe the entire cache table.
	cache.Flush()
}

简单看一下核心 api