为什么我的 Perl 单元测试在 EPIC 中失败但在调试器中工作?
有没有人经历过单元测试失败,当他们尝试调试它以找出失败发生的位置时,在调试器中运行代码时单元测试成功了?
我正在使用 Eclipse 3.5.1 与 EPIC 0.6.35 和 ActiveState ActivePerl 5.10.0。我用多个例程编写了模块 A 和模块 B。模块 B 中的例程调用模块 A 中的一堆例程。我将模拟对象添加到模块 B 单元测试文件中,以尝试在模块 B 上获得更完整的代码覆盖率,其中模块 B 中的代码测试以查看是否所有对模块 As 例程的调用失败或成功。因此,我在单元测试中添加了一些模拟对象,以强制某些模块 A 例程返回失败,但我没有按预期获得失败。当我调试单元测试文件时,对模块 A 例程的调用确实按预期失败(并且我的单元测试成功)。当我正常运行单元测试文件而不进行调试时,对模拟模块 A 例程的调用不会按预期失败(并且我的单元测试失败)。
这里可能发生了什么?如果我可以使用一小组简单的代码使其失败,我将尝试发布我的问题的工作示例。
附录:我将代码缩减到了能够演示我的问题的最低限度。该问题的详细信息和工作示例如下:
我的 Eclipse 项目包含一个“lib”目录,其中包含两个模块...MainModule.pm 和 UtilityModule.pm。我的 Eclipse 项目还在顶层包含一个名为 MainModuleTest.t 的单元测试文件和一个名为 input_file.txt 的文本文件,其中仅包含一些垃圾文本。
EclipseProject/
MainModuleTest.t
input_file.txt
lib/
MainModule.pm
UtilityModule.pm
MainModuleTest.t 文件的内容:
use Test::More qw(no_plan);
use Test::MockModule;
use MainModule qw( mainModuleRoutine );
$testName = "force the Utility Module call to fail";
# set up mock utility routine that fails
my $mocked = new Test::MockModule('UtilityModule');
$mocked->mock( 'slurpFile', undef );
# call the routine under test
my $return_value = mainModuleRoutine( 'input_file.txt' );
if ( defined($return_value) ) {
# failure; actually expected undefined return value
fail($testName);
}
else {
# this is what we expect to occur
pass($testName);
}
MainModule.pm 文件的内容:
package MainModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
use UtilityModule qw( slurpFile );
our @EXPORT_OK = qw( mainModuleRoutine );
sub mainModuleRoutine {
my ( $file_name ) = @_;
my $file_contents = slurpFile($file_name);
if( !defined($file_contents) ) {
# failure
print STDERR "slurpFile() encountered a problem!\n";
return;
}
print "slurpFile() was successful!\n";
return $file_contents;
}
1;
UtilityModule.pm 文件的内容:
package UtilityModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
our @EXPORT_OK = qw( slurpFile );
sub slurpFile {
my ( $file_name ) = @_;
my $filehandle;
my $file_contents = "";
if ( open( $filehandle, '<', $file_name ) ) {
local $/=undef;
$file_contents = <$filehandle>;
local $/='\n';
close( $filehandle );
}
else {
print STDERR "Unable to open $file_name for read: $!";
return;
}
return $file_contents;
}
1;
当我在 Eclipse 中右键单击 MainModuleTest.t 并选择 运行方式 | Perl Local,它给我以下输出:
slurpFile() was successful!
not ok 1 - force the Utility Module call to fail
1..1
# Failed test 'force the Utility Module call to fail'
# at D:/Documents and Settings/[SNIP]/MainModuleTest.t line 13.
# Looks like you failed 1 test of 1.
当我右键单击同一个单元测试文件并选择调试为 | Perl Local,它给了我以下输出:
slurpFile() encountered a problem!
ok 1 - force the Utility Module call to fail
1..1
所以,这显然是一个问题。运行方式和调试方式应该给出相同的结果,对吗?!?!?
Has anyone ever experienced a unit test that fails and when they tried to debug it to find out where the failure was occurring, the unit test succeeds when running the code in the debugger?
I'm using Eclipse 3.5.1 with EPIC 0.6.35 and ActiveState ActivePerl 5.10.0. I wrote module A and module B both with multiple routines. A routine in module B calls a bunch of routines from module A. I'm adding mock objects to my module B unit test file to try to get more complete code coverage on module B where the code in module B tests to see if all the calls to module As routines fail or succeed. So I added some mock objects to my unit test to force some of the module A routines to return failures, but I was not getting the failures as expected. When I debugged my unit test file, the calls to the module A routine did fail as expected (and my unit test succeeds). When I run the unit test file as normal without debugging, the call to the mocked Module A routine does not fail as expected (and my unit test fails).
What could be going on here? I'll try to post a working example of my problem if I can get it to fail using a small set of simple code.
ADDENDUM: I got my code whittled down to a bare minimum set that demonstrates my problem. Details and a working example of the problem follows:
My Eclipse project contains a "lib" directory with two modules ... MainModule.pm and UtilityModule.pm. My Eclipse project also contains at the top level a unit test file named MainModuleTest.t and a text file called input_file.txt which just contains some garbage text.
EclipseProject/
MainModuleTest.t
input_file.txt
lib/
MainModule.pm
UtilityModule.pm
Contents of the MainModuleTest.t file:
use Test::More qw(no_plan);
use Test::MockModule;
use MainModule qw( mainModuleRoutine );
$testName = "force the Utility Module call to fail";
# set up mock utility routine that fails
my $mocked = new Test::MockModule('UtilityModule');
$mocked->mock( 'slurpFile', undef );
# call the routine under test
my $return_value = mainModuleRoutine( 'input_file.txt' );
if ( defined($return_value) ) {
# failure; actually expected undefined return value
fail($testName);
}
else {
# this is what we expect to occur
pass($testName);
}
Contents of the MainModule.pm file:
package MainModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
use UtilityModule qw( slurpFile );
our @EXPORT_OK = qw( mainModuleRoutine );
sub mainModuleRoutine {
my ( $file_name ) = @_;
my $file_contents = slurpFile($file_name);
if( !defined($file_contents) ) {
# failure
print STDERR "slurpFile() encountered a problem!\n";
return;
}
print "slurpFile() was successful!\n";
return $file_contents;
}
1;
Contents of the UtilityModule.pm file:
package UtilityModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
our @EXPORT_OK = qw( slurpFile );
sub slurpFile {
my ( $file_name ) = @_;
my $filehandle;
my $file_contents = "";
if ( open( $filehandle, '<', $file_name ) ) {
local $/=undef;
$file_contents = <$filehandle>;
local $/='\n';
close( $filehandle );
}
else {
print STDERR "Unable to open $file_name for read: $!";
return;
}
return $file_contents;
}
1;
When I right-click on MainModuleTest.t in Eclipse and select Run As | Perl Local, it gives me the following output:
slurpFile() was successful!
not ok 1 - force the Utility Module call to fail
1..1
# Failed test 'force the Utility Module call to fail'
# at D:/Documents and Settings/[SNIP]/MainModuleTest.t line 13.
# Looks like you failed 1 test of 1.
When I right click on the same unit test file and select Debug As | Perl Local, it gives me the following output:
slurpFile() encountered a problem!
ok 1 - force the Utility Module call to fail
1..1
So, this is obviously a problem. Run As and Debug As should give the same results, right?!?!?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Exporter 和 Test::MockModule 都通过操作符号表来工作。那些同时做的事情并不总是能很好地结合在一起。在本例中,Test::MockModule 在 Exporter 将
slurpFile
的模拟版本导出到 MainModule 后将其安装到 UtilityModule 中。 MainModule 使用的别名仍然指向原始版本。要修复此问题,请将 MainModule 更改为使用完全限定的子例程名称:
这在调试器中起作用的原因是调试器还使用符号表操作来安装挂钩。这些挂钩必须在正确的时间以正确的方式安装,以避免正常情况下发生的不匹配。
任何时候代码在调试器中的行为与在调试器外运行时的行为不同时,都可以说这是一个错误(在调试器中),但是当您有三个模块都用符号表进行处理时,事情可能表现得很奇怪也就不足为奇了。
Both Exporter and Test::MockModule work by manipulating the symbol table. Things that do that don't always play nicely together. In this case, Test::MockModule is installing the mocked version of
slurpFile
into UtilityModule after Exporter has already exported it to MainModule. The alias that MainModule is using still points to the original version.To fix it, change MainModule to use the fully qualified subroutine name:
The reason this works in the debugger is that the debugger also uses symbol table manipulation to install hooks. Those hooks must be getting installed in the right way and at the right time to avoid the mismatch that occurs normally.
It's arguable that it's a bug (in the debugger) any time the code behaves differently there than it does when run outside the debugger, but when you have three modules all mucking with the symbol table it's not surprising that things might behave oddly.
您的模拟是否会操纵符号表?我在调试器中发现了错误干扰符号表的修改。虽然在我的例子中,问题是相反的;代码在调试器下崩溃,但在正常运行时工作。
Does your mocking manipulate the symbol table? I've seen a bug in the debugger that interferes with symbol table munging. Although in my case the problem was reversed; the code broke under the debugger but worked when run normally.