专注测试技术的课程订阅站点

单元测试用例该如何设计

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

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

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

我将通过提供真实的代码来进行演示。所有代码片段都将用 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 的浏览器,您需要为每个新版本更新驱动程序。

AI大语言模型在自动化用例生成中的探索

最近读到这篇文章,原文在是https://drlee.io/implementing-ai-in-software-testing-creating-a-text-generation-model-for-test-automation-7294b26f93c4,里面涉及到一些基于ai进行自动化测试的探索,原文是这么说的:

将人工智能 (AI) 纳入软件测试可谓是游戏规则的改变者,能够显著提升效率和有效性。本文利用 OpenAI 的文本生成模型——尤其是 GPT-3.5-turbo 和 GPT-4-turbo-preview——在 Google Colab 中构建了一个文本生成模型,重点关注测试自动化用例。

看了一下,里面列举了 3 个测试用例。

The system shall allow users to securely login with a username and password.

这是用户登录的用例。

Ensure that the shopping cart allows users to add items, remove items, and proceed to checkout.

这是购物车的用例。

The weather API should return a JSON response with fields for temperature, humidity, and precipitation forecast for the next 5 days.