Perl中Test::Builder子测试使用is_deeply报错的解决求助
问题背景
我正尝试将代码从use Test::More迁移到use Test::Builder,在Perl 5.18.2环境下调用is_deeply时遭遇报错:
Cannot run test (h_c: is_deeply($n, $s)) with active children at /usr/lib/perl5/5.18.2/Test/More.pm line 1020
由于Test::Builder本身没有is_deeply方法,我通过use Test::More 'import' => [qw(is_deeply)]仅导入该方法,但在子测试($tb->child())中调用时程序崩溃。
调试后发现,is_deeply内部创建的Test::Builder实例并非当前子测试的实例,而是全局实例——该实例存在未结束的一级子测试,导致Test::Builder::ok中的检测失败。我已复现问题的最小示例,还发现使用Test::Builder->new而非create会触发问题,临时通过local $Test::Builder::Test = $TB可解决但存在风险,请问是否有在子测试中使用is_deeply的正确方法?
解决方案
方法1:用Test::Builder::Module封装测试函数
创建自定义测试模块继承Test::Builder::Module,它会自动将导入的测试函数绑定到当前活跃的Test::Builder实例,避免全局实例冲突:
package MyTest; use base 'Test::Builder::Module'; use Test::More 'import' => [qw(is_deeply)]; 1;
测试脚本中使用:
use Test::Builder; use MyTest; my $tb = Test::Builder->create; $tb->plan(tests => 1); my $child = $tb->child('子测试'); $child->plan(tests => 1); is_deeply([1,2,3], [1,2,3], '深层结构对比'); $child->finalize; $tb->finalize;
方法2:调用is_deeply时指定子测试实例
Perl 5.18.2版本的Test::More中,is_deeply支持通过builder参数指定使用的Test::Builder实例,直接在调用时传入子测试对象即可:
use Test::Builder; use Test::More 'import' => [qw(is_deeply)]; my $tb = Test::Builder->create; $tb->plan(tests => 1); my $child = $tb->child('子测试'); $child->plan(tests => 1); is_deeply([1,2,3], [1,2,3], '深层结构对比', { builder => $child }); $child->finalize; $tb->finalize;
方法3:改用Test::More的subtest函数
如果项目允许,推荐直接使用Test::More原生的subtest创建子测试,所有测试函数会自动关联正确的实例,无需手动管理Test::Builder对象:
use Test::More tests => 1; subtest '子测试' => sub { plan tests => 1; is_deeply([1,2,3], [1,2,3], '深层结构对比'); };
临时方案的风险说明
你提到的local $Test::Builder::Test = $TB虽然能临时解决问题,但会全局替换当前活跃的Test::Builder实例,若测试中存在嵌套子测试或并发逻辑,可能导致测试计数错误、结果混乱等问题,不建议在正式测试代码中使用。
内容的提问来源于stack exchange,提问作者U. Windl

