AI人工智能与软件测试

测试同学绩效不好怎么办

今天是 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

Slenium已死?

selenium 已死?其他的框架例如 playwright, cypress 当立?这是去年一个广泛讨论的话题。对于我来说这个观点很明显是偏颇的,因为

  • selenium 本身已经成为了 w3c 规范的一部分,现在市面上所有的浏览器都遵循这个规范
  • selenium 本来就不是一个纯测试工具,它是为自动化而生,除了浏览器测试之外,selenium 还有很广泛的用途,而 playwright/cypress 则更专精于测试领域
  • selenium 的 api 相当稳定,对于一些需要长期维护的项目来说这是非常有诱惑力的,而我去年用 playwright 做了一个项目,今年由于 api 升级,去年的代码基本上已经完全不可用了

所以对我来说,selenium 尽管已经徐娘半老,吸引力大不如前,但在某些场景下,selenium 仍然会是我的首选工具,就像是 vb/php 一样,尽管大家都已经看衰很多年了,但这些技术一直没有落幕退场。

selenium 官方可能也察觉到了这些广泛的讨论,昨天他们官网 blog 发了一篇文章,直接讨论 selenium 与其他工具的情感纠葛,上下文可能是不少人发博文比较 selenium 与其他工具,然后标题党一下,使得大家产生错觉:selenium 真的已经快死翘翘了。这篇内容专业简洁,适合给大家消除误解,原文地址:https://www.selenium.dev/blog/2024/selenium-vs-blog-posts/。

下面是全文翻译。

这篇博文讨论了那些比较 Selenium、Cypress 和 Playwright 的标题党文章。这些文章没有意义,也没有帮助。

作者:David Burns (@AutomatedTester) | 2024 年 1 月 9 日星期二

在博文中,关于自动化测试的标题党文章最容易的方式就是将 Selenium 与其他工具进行比较,并配以一个吸引人的标题,尤其是当它贬低现有工具时。

不幸的是,这可能会使人们对这些产品中的哪些功能可用产生困惑,尤其是当我们进行同类的过度类比时。

Selenium 一直是一个很好的浏览器自动化工具。对于该项目来说,幸运的是,它已经成为测试 Web 应用程序的首选工具近 20 年。该项目专注于构建越来越复杂的浏览器自动化的难点。项目的重点一直是稳定的 API 和可扩展性,以保证 Selenium 的运行。它没有关注人们如何进行测试,因为有非常好的测试框架可用,并且为 5 种不同的编程语言进行测试是一项非常重要的工程工作。

然而,这些博文中经常出现一些误解。

与 Playwright 和 Cypress 相比,设置浏览器和驱动程序太困难

过去确实如此,因为您需要下载驱动程序。对于 GeckoDriver 和 SafariDriver 来说,这并不太糟糕,因为它们可以优雅地处理浏览器升级。另一方面,对于基于 Chromium 的浏览器,您需要为每个新版本更新驱动程序。