资讯动态

Dragonfly 系统测试指南:Pytest 单元测试与 Docker 客户端集成测试实战

发布时间:2026/9/11 5:30:55 来源:尧图企业网站定制
Dragonfly 系统测试指南Pytest 单元测试与 Docker 客户端集成测试实战【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly本指南以 tests/README.md 为核心系统讲解 DragonflyRedis / Memcached 的现代替代品的两大测试体系基于 Pytest 的 Python 系统测试以及基于 Docker 的第三方客户端集成测试。读完本文你将掌握如何定位dragonfly二进制、使用df_server/client/async_client/pool/async_pool等核心 fixture 编写测试通过dfly_args与dfly_multi_test_args为实例注入启动参数并用--gdb、--df、--repeat等命令行开关调试问题同时学会如何构建 node-redis、ioredis、Jedis 等客户端容器镜像在 Docker 中跑通跨生态的兼容性验证。1. 测试体系总览Dragonfly 仓库的测试分为两个层次位于 tests/ 目录下Pytest 系统测试tests/dragonfly/直接以子进程方式启动dragonfly二进制用 Python 客户端redis、redis.asyncio、pymemcache等对其发起真实命令覆盖连接、快照、复制、集群、搜索、流式结构、发布订阅等几乎所有功能模块集成测试tests/integration/为 node-redis、ioredis、Jedis 等第三方 Redis 客户端分别提供 Dockerfile在容器内运行客户端官方测试套件来验证与 Dragonfly 的兼容性Docker 容器默认假设 Dragonfly 运行在localhost:6379。此外仓库还提供了丰富的辅助工具fuzz/ 目录下的模糊测试种子与变异器、tests/dragonfly/seeder/ 中的数据填充器Seeder、tests/dragonfly/instance.py 中的实例管理类以及 tests/dragonfly/proxy.py 的网络代理。系统测试框架本身遵循「share nothing」的隔离策略力求每个测试互不产生副作用。2. 环境准备找到 dragonfly 二进制并安装 Python 依赖2.1 二进制路径约定Pytest 测试默认假定dragonfly二进制位于仓库根目录的build-dbg目录中即root/build-dbg/dragonfly。这一逻辑可以在 tests/dragonfly/conftest.py 中看到path os.environ.get(DRAGONFLY_PATH, os.path.join(scripts_dir, ../../build-dbg/dragonfly))如果二进制放在其他位置可以通过DRAGONFLY_PATH环境变量覆盖export DRAGONFLY_PATH/path/to/your/dragonfly pytest -xv dragonfly2.2 Python 版本与虚拟环境测试需要 Python 3。如果你的机器同时装有 Python 2 和 Python 3务必显式调用python3python3 -m pytest -xv dragonfly官方建议使用 Python 虚拟环境。创建并激活后安装 tests/dragonfly/requirements.txt 中声明的全部依赖python3 -m venv venv source venv/bin/activate pip3 install -r dragonfly/requirements.txt如果你偏好无需激活环境的项目管理方式也可以使用uvAstral 出品。在tests目录下初始化一次项目本地环境uv venv uv run pytest -s dragonfly/connection_test.py依赖清单中值得关注的核心库包括pytest7.1.2、pytest-asyncio0.20.1固定版本、pytest-repeat支持--repeat、redis5.2.1、pymemcache、meta_memcache、prometheus_client用于解析/metrics、psutil用于探测进程端口与日志、aiohttp以及用于对象存储测试的boto3、azure-storage-blob和用于搜索功能的redis-om、numpy、ml_dtypes等。pytest.ini中通过asyncio_modeauto开启了异步模式并默认addopts -ra --emoji --showlocals -m not large即默认排除large标记的重型测试。3. 运行 Pytest 系统测试3.1 全量与选择性运行在tests目录下运行全部测试pytest -xv dragonfly只运行名称中包含特定子串的测试-k做子串过滤这是 pytest 原生的选择机制pytest -xv dragonfly -k substring例如只跑connection_test.py中名为test_subscribe的用例同时把 Dragonfly 的日志打到 stdout、开启dragonfly_connection模块的 vmodule 日志级别 2便于观察连接内部流程pytest dragonfly/connection_test.py -s --df logtostdout --df vmoduledragonfly_connection2 -k test_subscribe3.2 自定义命令行参数pytest 插件级选项测试框架在 conftest.py 中通过pytest_addoption注册了一系列自定义开关其中与 README 直接对应的有选项说明--gdb所有实例都在 gdb 中启动便于断点调试gdb 启动较慢等待超时会放宽--df argval向所有 Dragonfly 实例传递自定义参数可多次使用--log-seeder file将最近测试中 seeder 产生的所有单数据库命令记录到指定文件用于回放复现--existing-port不新起实例而是连接一个已运行的 Dragonfly 进程端口来跑测试--rand-seed设置全局随机种子使 seeder 的数据生成可预测、可复现--repeat N将每个测试重复运行 N 次配合pytest-repeat便于定位偶发失败--repeat的实现方式很有参考价值pytest_generate_tests钩子会向metafunc.fixturenames追加一个tmp_ctfixture再用metafunc.parametrize(tmp_ct, range(count))将每个测试函数参数化展开从而在不修改任何测试代码的前提下实现「重复运行」。conftest.py除 README 提到的选项外conftest 还提供了--existing-admin-port、--existing-mc-port连接已运行的 admin / memcached 端口、--direct-out不后处理 Dragonfly 输出、--buffered-output缓冲实例输出便于分组、--drop-data-after-each-test在--repeat循环中每次测试后即清理数据避免磁盘被撑满等进阶开关。4. 核心 fixture与 Dragonfly 实例交互所有 fixture 都定义在 tests/dragonfly/conftest.py 中并且通过 tests/dragonfly/init.py 的dfly_args/dfly_multi_test_args装饰器与df_factory参数化机制联动。4.1 五个基础 fixturedf_server默认的 Dragonfly 实例测试中直接可用。它由df_factory创建并start()在df_factoryclass 作用域内共享因此相同参数配置的测试会复用同一个实例client与async_client连接到默认实例的同步 / 异步客户端。每个新的 client 都会先flushall()清空实例保证测试间数据隔离见cluster_client、async_client的实现都先flushall再select(DATABASE_INDEX)conftest.pypool与async_pool连接到默认实例的客户端连接池。async_pool构造为aioredis.ConnectionPoolmax_connections32decode_responsesTrue测试结束会disconnect(inuse_connectionsTrue)断开全部连接conftest.py。4.2 实例默认参数从源码确认从 instance.py 的DflyInstanceFactory.create()可以看到每个测试实例默认携带的启动参数这对理解测试行为很有帮助dbfilename默认不落盘避免测试间相互污染noversion_check跳过版本检查maxmemory8GmacOS 不会自动设置必须显式给出vmoduledragonfly_connection1,db_slice1,listener_interface1,...默认开启一组关键模块的详细日志log_dir指向该测试类的日志目录/tmp/dragonfly_logs/class见df_log_dirfixturenum_shards若未显式指定会默认设为proactor_threads - 1当线程数 1 时以便暴露更多分片相关的并发缺陷若未指定port实例会以--port-1启动让 Dragonfly 自己挑选随机空闲端口测试框架再用psutil探测实际监听端口get_port_from_psutil。DflyInstance还提供port/admin_port/mc_port属性对应--port、--admin_port、--memcached_port参数以及metrics()抓取并解析http://localhost:port/metrics的 Prometheus 指标、find_in_logs(pattern)实例停止后按正则检索日志文件、rss当前常驻内存等调试辅助方法。4.3 df_seeder_factory可复现的数据填充器df_seeder_factoryfixture 从--rand-seed读取种子若未提供则取random.randrange(sys.maxsize)生成随机种子并打印到日志便于复现失败conftest.py。--log-seeder则会把 seeder 产生的命令记录到文件配合--rand-seed可以精确回放数据生成过程。5. 编写测试装饰器、发现规则与实例5.1 测试发现规则pytest 会递归扫描tests/dragonfly目录匹配test_*.py或*_test.py文件名以及以下函数/方法命名规则类外以test前缀命名的函数类内以test前缀命名、且类名以Test开头类不允许定义__init__方法的方法。注意在tests/dragonfly下新建子目录时必须创建__init__.py文件否则可能出现同名模块冲突pytest 官方建议的「tests outside application code」最佳实践。Dragonfly 的做法是在包内直接提供共享工具例如 tests/dragonfly/init.py 中的dfly_args、dfly_multi_test_args与PortPicker。5.2 用 dfly_args 传递单一参数配置dfly_args用于给当前测试或测试类指定一组Dragonfly 启动参数。它本质上是对df_factoryfixture 的pytest.mark.parametrize(..., indirectTrue)参数化封装init.py。示例单线程实例下验证事务队列不会乱序执行dfly_args({proactor_threads: 1}) async def test_txq_ooo(async_client: aioredis.Redis, df_server): ...见 tests/dragonfly/generic_test.py。5.3 用 dfly_multi_test_args 测试多组配置dfly_multi_test_args允许一次指定多组参数配置每一组配置会创建一个独立的 Dragonfly 实例测试函数分别拿到各自实例的客户端。例如验证keys_output_limit取 512 与 1024 两种配置下的 KEYS 截断行为dfly_multi_test_args({keys_output_limit: 512}, {keys_output_limit: 1024}) class TestKeys: async def test_max_keys(self, async_client: aioredis.Redis, df_server): max_keys df_server[keys_output_limit] pipe async_client.pipeline() batch_fill_data(pipe, gen_test_data(max_keys * 3)) await pipe.execute() keys await async_client.keys() assert len(keys) in range(max_keys, max_keys 512)见 tests/dragonfly/generic_test.py。注意df_server[keys_output_limit]这种下标访问方式——DflyInstance.__getitem__会返回实例实际使用的启动参数值instance.py。5.4 参数中的环境变量插值装饰器参数支持格式化字符串{VAR}会被替换为环境变量VAR的值。由于 pytest 目前的限制fixture 不能直接传入装饰器因此这是把临时目录路径等动态值传入 CLI 参数的官方推荐方式。在 snapshot_test.py 中可以看到典型用法BASIC_ARGS {dir: {DRAGONFLY_TMP}/, proactor_threads: 4}这里的{DRAGONFLY_TMP}来自test_envfixture——它会把临时目录注入环境变量DRAGONFLY_TMPconftest.py随后DflyInstanceFactory.create()中args[k].format(**self.params.env)完成替换instance.py。5.5 三个参考测试文件snapshot_test.py综合演示dfly_args、环境变量插值以及测试前准备如通过BASIC_ARGS指定dir、构造 Azurite/MinIO 对象存储环境、用 seeder 灌数据后做SAVE并校验快照文件;generic_test.py演示dfly_multi_test_args多配置测试以及通过df_factory.create(...)在测试内按需创建带特定参数如requirepass的独立实例connection_test.py多异步连接并发场景的测试范例包含CollectingMonitor这类借助MONITOR命令采集服务端消息、并利用ECHO标记同步就绪状态的实现还针对enable_resp_io_loop_v2新 IO 循环做了能力探测is_resp_io_loop_v2。5.6 编写自己的 fixture所有 fixture 都集中在 conftest.py 中。新增 fixture 前先确认是否已有现成实现新 fixture 的作用域应尽量小保证测试彼此独立、无副作用——这正是仓库奉行的「share nothing」策略与 docs/df-share-nothing.md 的设计理念一脉相承。5.7 管理依赖新增依赖必须同步到 tests/dragonfly/requirements.txt。可以在tests/dragonfly目录下用以下命令生成完整清单pip3 freeze requirements.txt6. 集成测试在 Docker 中跑第三方客户端集成测试位于 tests/integration/ 目录。每个被测客户端包提供一个独立的 Dockerfile容器内包含运行其官方测试套件所需的全部环境测试时假设 Dragonfly 已在本机localhost:6379上运行。通用运行方式docker build -t [test-name] -f [test-dockerfile-name] . docker run --networkhost [test-name]使用--networkhost是为了让容器直接访问宿主机上监听 6379 端口的 Dragonfly 实例。6.1 node-redis针对 node-redis 客户端的集成测试构建文件tests/integration/node-redis.Dockerfile。该 Dockerfile 基于node:18.7.0克隆 Dragonfly 维护的 node-redis fork 分支构建测试工具后以npm run test -w ./packages/client -- --redis-version2.8作为启动命令。构建与运行docker build -t node-redis-test -f ./node-redis.Dockerfile . docker run --networkhost node-redis-test只跑选定测试时可借助-g regex按 mocha 的 grep 规则过滤--redis-version指定协议版本这里为 2.8docker run --networkhost node-redis-test npm run test -w ./packages/client -- --redis-version2.8 -g regexmocha 框架的其他命令行选项也可以按此方式追加传入。6.2 ioredisioredis 是 Node.js 生态中性能导向、功能全面的 Redis 客户端自带非常庞大的测试覆盖。目前 Dragonfly 尚未支持其全部特性因此 README 明确不要脱离 Docker 镜像直接运行 ioredis 测试——镜像会锁定正确版本并对部分用例打补丁。官方推荐通过脚本 run_ioredis_on_docker.sh 运行。如果镜像已经构建好直接执行./integration/run_ioredis_on_docker.sh更稳妥的做法是加--build先重新构建或确保镜像再执行测试./integration/run_ioredis_on_docker.sh --build也可以手动构建镜像后单独运行docker build -t ioredis-test -f ./ioredis.Dockerfile . docker run --rm -i --networkhost ioredis-test ./run_tests.sh脚本会执行docker run --rm -i --networkhost ioredis-test ./run_tests.sh其中run_tests.sh是由ADD .run_ioredis_valid_test.sh run_tests.sh注入的受限测试清单ioredis.Dockerfile因此只运行当前 Dragonfly 已支持的用例未支持的如集群、Elasticache 相关会被跳过。关于 Dockerfile 中 ENTRYPOINT 与 ioredis 项目package.json里 npm test 脚本的对应关系可对照两者源码理解。需要说明的背景目前 ioredis 测试中 MONITOR 命令用例会失败因为 Dragonfly 始终以大写返回命令名而测试期望小写这也是用打补丁镜像运行的原因之一。6.3 JedisJedis 集成测试的构建文件是 tests/integration/jedis.Dockerfile基于maven:3.8.6-jdk-11克隆 Dragonfly 维护的 jedis fork 分支先用mvn test -DskipTests完成编译启动命令则通过mvn surefire:test -Dtest...只执行一组选定测试类覆盖各类值命令、位命令、控制命令、哈希、列表、脚本、集合、事务、客户端、发布订阅、有序集合、排序与流命令。构建与运行docker build -t jedis-test -f ./jedis.Dockerfile . docker run --networkhost jedis-test7. 常见调试技巧与故障排查定位二进制确认DRAGONFLY_PATH指向有效的 dragonfly 可执行文件否则会报「Failed to start instance」。若使用--existing-port测试框架将直接复用该端口上的进程不再启动新实例此时start()直接返回见 instance.py复现偶发失败--rand-seed固定随机种子 --log-seeder file记录 seeder 命令再配合--repeat N反复执行以放大问题观察服务端日志--df logtostdout --df vmodulemodulelevel把日志打到 stdout 并针对某模块提高日志级别。实例日志默认写入/tmp/dragonfly_logs/测试类名/测试失败时框架会把日志复制到/tmp/failed/便于归档分析见copy_failed_logs与pytest_runtest_makereport钩子conftest.pygdb 调试--gdb让实例在 gdb 内启动若进程无法优雅终止框架会发送SIGUSR1触发 Dragonfly 打印栈随后自动addr2line符号化栈地址见DflyInstance.stop()与symbolize_stack_traceinstance.py;S3 / 对象存储相关测试conftest 支持MINIO_S3_ENDPOINT环境变量——设置后会自动下载并拉起一个本地 MinIO 服务若网络允许创建dragonfly-testbucket并把DRAGONFLY_S3_BUCKET、AWS_*等环境变量注入测试进程同时为 Dragonfly 实例追加s3_endpoint相关启动参数conftest.py、instance.py。该能力服务于snapshot_test.py中的 S3 快照上传下载用例TLS 测试conftest 提供with_tls_server_args、with_ca_tls_server_args、with_tls_client_args等 fixture用gen_ca_cert/gen_certificate现场生成 CA 与服务器/客户端证书覆盖 TLS 加密与双向认证场景conftest.py。8. 测试标记与 CI 约定tests/pytest.ini 中定义了多个自定义 marker写测试时可按需使用CI 也会据此分流opt_only仅适合在优化release构建下运行的测试如复制压力测试CI 的 regression/release 工作流才会执行exclude_epoll已知在 epoll 事件循环下失败的测试epoll 工作流会跳过debug_only仅适合 debug 构建release 构建太快导致断言无法触发large重型测试需要大规格 CI runner默认通过-m not large排除replication配置replicationfixture 的拓扑如master_args、replica_args、replicas关键字参数会转发给 replication_utils.py 的setup_replication。replicationfixture 本身也支持按需切换拓扑例如在复制一致性测试中动态指定主从参数从而覆盖从单机到主从、再到集群的多形态部署验证。9. 小结本文完整梳理了 Dragonfly 的两级测试体系以 conftest.py 为核心的 Pytest 系统测试二进制定位、五个基础 fixture、装饰器参数注入、可复现 seeder、失败日志归档以及以 Docker 为边界的第三方客户端集成测试node-redis / ioredis / Jedis 的构建与运行。无论你是想为 Dragonfly 提交一个新功能并补齐测试还是想在自己的环境中复现某个用例都可以直接参考 tests/README.md 与本仓库 tests/ 下的真实实现开始动手。【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价