为什么我的Perl单元测试在EPIC中失败但在调试器中工作?

时间:2009-11-18 21:22:37

标签: perl unit-testing debugging mocking epic

有没有人经历过单元测试失败,当他们尝试调试它以找出发生故障的位置时,在调试器中运行代码时单元测试会成功吗?

我正在使用带有EPIC 0.6.35和ActiveState ActivePerl 5.10.0的Eclipse 3.5.1。我用多个例程编写了模块A和模块B.模块B中的例程从模块A调用一堆例程。我将模拟对象添加到模块B单元测试文件中,以尝试在模块B上获得更完整的代码覆盖,其中模块B中的代码测试以查看是否所有调用模块因为例程失败或成功。所以我在单元测试中添加了一些模拟对象来强制一些模块A例程返回失败,但我没有按预期得到失败。当我调试单元测试文件时,对模块A例程的调用确实按预期失败(并且我的单元测试成功)。当我正常运行单元测试文件而不进行调试时,对模拟的模块A例程的调用不会按预期失败(并且我的单元测试失败)。

这可能会发生什么?如果我可以使用一小组简单的代码让它失败,我会尝试发布我的问题的一个工作示例。

ADDENDUM: 我将我的代码缩小到最低限度,以说明我的问题。详细信息和问题的工作示例如下:

我的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并选择 Run As |时 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.

当我右键单击相同的单元测试文件并选择 Debug As |时 Perl Local ,它为我提供了以下输出:

slurpFile() encountered a problem!
ok 1 - force the Utility Module call to fail
1..1

所以,这显然是一个问题。运行方式和调试方式应该给出相同的结果,对吧?!?!?

2 个答案:

答案 0 :(得分:1)

Exporter和Test :: MockModule都可以通过操作符号表来工作。这样做的事情并不总能很好地融合在一起。在这种情况下,在导出器已将其导出到MainModule之后,Test :: MockModule正在将模拟版本的slurpFile安装到UtilityModule 中。 MainModule正在使用的别名仍然指向原始版本。

要修复它,请更改MainModule以使用完全限定的子例程名称:

 my $file_contents = UtilityModule::slurpFile($file_name);

这在调试器中起作用的原因是调试器使用符号表操作来安装挂钩。必须以正确的方式和时间安装这些挂钩,以避免正常发生的不匹配。

可以说这是一个错误(在调试器中),任何时候代码的行为都不同于在调试器外部运行时的错误,但是当你有三个模块都在使用符号表时,事情可能表现得很奇怪就不足为奇了

答案 1 :(得分:0)

你的模拟操纵符号表吗?我看到bug in the debugger会干扰符号表的修改。虽然在我的情况下问题被逆转了;代码在调试器下破坏,但在正常运行时工作。