在为您的项目开发测试时,迟早您会遇到某些测试随机失败的情况。这些测试也称为不稳定测试,非常令人头疼,因为您永远不知道失败是随机的还是代码中存在回归。最糟糕的情况是您会忽略这些测试,因为您知道它们不稳定。大多数测试框架甚至有专门的注解或其他方式来表示测试不稳定,如果失败,应该忽略失败。这种测试的价值非常值得怀疑。对这种测试最好的处理方式当然是修复它,使其不再随机失败。这说起来容易,做起来难。最困难的部分通常是让测试在您的开发环境中失败,以便您可以对其进行调试,了解它为什么失败以及失败的根本原因。在这篇博文中,我将尝试展示一些可以帮助您在本地计算机上模拟随机测试失败的技术。
根据我的经验,导致测试随机失败的最常见原因是没有妥善清理的环境或运行缓慢的环境。这两种情况在 CI 环境中都很常见。过去,由于与其他测试的干扰而导致的失败更为常见。如今,随着虚拟机和容器的广泛使用,这通常已不再是问题。此外,各种 CI 服务平台实现的测试隔离做得也很好。使用 CI 服务平台的一个缺点是,您通常无法登录到机器并在那里调试测试。因此,您要么需要启用调试日志并等待下一次失败,要么猜测原因并尝试在本地机器上模拟它。
在 CI 环境中导致随机失败的最常见根本原因是各种类型的缓慢。这是虚拟环境中资源过度承诺或对 VM/容器设置资源限制的结果。因此,在本地模拟随机测试失败的最有效方法之一是限制本地环境的测试资源。让我们看看常见的选项以及如何做到这一点。
在单个线程中运行测试
减慢测试速度的一种可能方法,尤其是在您的应用程序是多线程的情况下,是通过单个线程或有限数量的线程来执行测试。在 Linux 操作系统上,使用 taskset 命令非常容易。 taskset 命令告诉 Linux 调度程序将给定进程附加到指定的 CPU 核心。要在一个 CPU 核心上运行,例如 Debezium MySQL TransactionMetadataIT,您只需要运行
taskset -c 0 mvn verify -DskipTests -Dit.test=DebeziumEngineIT 这将会在 CPU #0 上执行测试。
限制容器资源
在 Debezium 项目中,我们大量使用容器进行测试。我们运行数据库,这些数据库是与容器中运行的测试相对的。我们经常需要的是减慢测试本身的速度,而不是减慢数据库的速度。 Docker 提供了很多 选项 来限制容器资源。最有用的是通常使用 --cpus 参数来限制 CPU。这允许我们限制 Docker 可以用于运行容器的 CPU 量。这里的好处是它可以是浮点数,所以您可以通过设置 --cpus=0.5 来限制容器只使用一半的 CPU 时间。以类似的方式,您还可以限制其他资源,例如 RAM。
常见的 Debezium 工作流程是通过 Docker Maven 插件 从 Maven 运行容器。该插件提供了 长串属性 来配置,包括限制容器资源的属性。但是,此选项有一个警告。在当前版本中,docker.cpus 期望 长整型数字 而不是浮点数,其含义大致是容器在一个秒周期内可以使用的 CPU 纳秒数。例如,相当于 --cpus=0.5 的值将是
mvn docker:start -Ddocker.cpus=500000000 此问题最近已被 修复,并且应该包含在下一个 Docker Maven 插件版本中。因此,一旦 Debezium 升级到新版本,您应该就可以像从命令行运行容器时那样使用 docker.cpus。其他 Docker Maven 插件属性,例如 docker.memory,应该按预期工作。
施加网络延迟
随机测试失败的另一个常见来源是网络延迟。在本地机器上模拟它可能没有简单的方法,并且需要使用某种代理。幸运的是,有一个专门为此目的设计的代理 - Toxiproxy。它是一个专门用于模拟各种网络故障和延迟的代理。它具有丰富的功能集,而且设置起来相当容易,因此使用起来非常愉快。让我们看看如何在本地机器上将其与 Debezium 测试结合使用。
您可以在本地安装 Toxiproxy(在 Fedora 上通过运行 sudo dnf install toxiproxy)或在容器中下载它
docker pull ghcr.io/shopify/toxiproxy 我们将要在容器中运行 Toxiproxy,但本地安装它也很方便,因为它包含一个 CLI 工具来向 Toxiproxy 发送命令。否则,我们就必须从容器中运行命令。为简单起见,我们将使用本地安装的 CLI 工具。 Toxiproxy 允许我们通过 HTTP 发送命令,并在端口 8474 上监听。因此,当我们启动 Toxiproxy 时,我们需要公开此端口。我们需要公开的另一个端口是数据库的端口,Toxiproxy 将作为该数据库的代理。在我们的示例中,我们将使用 MySQL,因此我们需要公开端口 3306。当然,我们可以使用任何其他端口,但那样的话,我们就需要向 Debezium 测试传递一个额外的参数,即 database.port,指向 Toxiproxy 公开的端口。再次,为简单起见,我们坚持使用默认端口 3306。此外,由于我们将在本地机器上(而不是在容器中)运行 Debezium 测试,因此我们需要将 Toxiproxy 附加到 localhost 网络,该网络默认命名为 host。将所有这些结合起来,我们可以运行 Toxiproxy 容器,如下所示:
docker run --rm -p 8474:8474 -p 3306:3306 --net=host -it ghcr.io/shopify/toxiproxy 现在我们还需要启动我们的数据库。由于端口 3306 已被 Toxiproxy 占用,因此我们必须选择另一个端口,比如说 3307
mvn docker:start -Dmysql.port=3307 最后要配置的是告诉 Toxiproxy 它应该为哪些端口创建代理。在我们的例子中,它将从端口 3306(监听端口 -l)到 3307(上游端口 -u)
toxiproxy-cli create mysql -l 0.0.0.0:3306 -u 0.0.0.0:3307 此命令将在 Toxiproxy 中创建一个名为 mysql 的新代理。可以有多个代理。我们可以通过运行以下命令来列出所有代理:
toxiproxy-cli list 这将产生类似这样的输出:
$ toxiproxy-cli list Name Listen Upstream Enabled Toxics ====================================================================================== mysql [::]:3306 0.0.0.0:3307 enabled None
现在让我们尝试一下是否一切正常,然后运行一些测试
mvn verify -DskipTests -Ddatabase.hostname=localhost -Pskip-integration-tests -Dit.test=TransactionMetadataIT 一切都应该像正常一样运行,因为我们还没有创建任何毒素(延迟或故障)。这只是一个检查代理是否正常工作的检查。如果一切正常,让我们创建一个毒素:
toxiproxy-cli toxic add mysql --type latency --attribute latency=500 -n mysql_latency 这将为 mysql 代理添加 500 毫秒的网络延迟。该毒素命名为“mysql_latency”。
您可以通过运行 inspect 命令来获取指定代理的更多详细信息:
toxiproxy-cli inspect mysql 输出如下:
$ toxiproxy-cli inspect mysql Name: mysql Listen: [::]:3306 Upstream: 0.0.0.0:3307 ====================================================================== Upstream toxics: Proxy has no Upstream toxics enabled. Downstream toxics: mysql_latency: type=latency stream=downstream toxicity=1.00 attributes=[ jitter=0 latency=500 ]
现在,再次运行测试。您是否注意到测试运行的时间大大延长了?如果是,那么一切都按预期工作,因为我们为每次数据库调用都添加了延迟。
这是为 Toxiproxy 添加毒素的一个简单示例。 Toxiproxy 提供了更多选项和配置毒素的方法。有关更多详细信息,请参阅 Toxiproxy。
完成后,我们可以删除毒素:
toxiproxy-cli toxic remove mysql -n mysql_latency 以及代理本身:
toxiproxy-cli delete mysql 或者简单地停止并删除容器。
总结
在这篇博文中,我试图展示一些可以帮助您在本地模拟不稳定测试失败的技术。所有这些技术都试图通过限制 CPU 或使用 Toxiproxy 施加网络延迟来使测试环境响应性降低。您的应用程序堆栈的许多部分都可能导致测试不稳定,并且还有许多其他工具可以注入各种类型的故障(例如,磁盘故障)。所以这篇文章远非详尽。但我希望它能帮助您调试至少一些不稳定测试,即使不是 Debezium 项目中的,至少也是您自己项目中的。
所有这些东西,特别是 Toxiproxy,也可以常规使用,即使是在 CI 中,也能发现项目中只有当运行它的环境行为不当时才会出现的各种隐藏问题。
欢迎在讨论中分享您关于如何调试不稳定测试的其他技巧以及您认为好用的工具。
关于 Debezium
Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。