【问题标题】:Is there a way to override a Perl "use constant" in your unit testing?有没有办法在单元测试中覆盖 Perl“使用常量”?
【发布时间】:2010-11-07 09:01:09
【问题描述】:

我有一个 Perl 模块,我声明了一些常量:

use constant BASE_PATH => "/data/monitor/";

在实时操作中,常量将永远改变,但我希望能够在我的单元测试中修改它,例如将其设置为~/project/testdata/。有没有办法做到这一点而不必使用全局可变变量?

我可以在constant 上使用Test::MockObject 吗?

【问题讨论】:

  • 如果您需要在整个测试过程中更改它,为什么不将其设为“非常量”
  • 你可以试试猴子补丁常量的导入子程序。
  • 你为什么要这样做而不是有一个配置文件?
  • 我发现自己处于同样的位置。请参阅我的解决方案:答案晚了 10 年。 stackoverflow.com/a/58920320/124486 如果它是最好的,请考虑将其标记为已选择。

标签: unit-testing perl mocking constants compile-time-constant


【解决方案1】:

当使用常量时,它们被实现为常量函数,行为如下:

use subs 'BASE_PATH';
sub BASE_PATH () {"/data/monitor/"}

程序中对 BASE_PATH 的任何使用都是内联的,因此无法修改。

要达到类似的效果,您可以手动使用 subs pragma(使 BASE_PATH 表现为内置函数)并将 BASE_PATH 声明为标准函数:

use subs 'BASE_PATH';
sub BASE_PATH {"/data/monitor/"}

print "BASE_PATH is ".BASE_PATH."\n";

*BASE_PATH = sub {"/new/path"};
print "BASE_PATH is ".BASE_PATH."\n";

虽然我不太确定你为什么要这样做。

【讨论】:

    【解决方案2】:

    显然,如果您的 BASE_PATH 定义在您的测试确实更改之前使用/编译到另一个子例程中(通过

    *BASE_PATH = sub { ... }
    

    或其他东西)您没有解决方案(因为当原始模块使用 BASE_PATH 作为常量时,它确实定义了一个 INLINE 函数,该函数在其他代码中使用时是内联的)

    【讨论】:

    • 唯一的解决方案是猴子补丁常量的导入子程序。围绕常量的导入安装一个包装器,它拦截要覆盖的指定常量名称和要应用的值。如果你这样做,那么只有被覆盖的常量 subs 将被创建并且它们将被编译器内联。我现在没有时间写这样的东西,但应该不会太难。
    • 但在这种情况下,制作自己的不会让子内联的 constant.pm 会更好/更容易,不是吗?把它放在测试库路径和ta-da的早期位置......
    • 您可以停止内联。看我的回答。 stackoverflow.com/a/58920320/124486
    【解决方案3】:
    包我的::类; 使用常量 BASE_PATH => "/a/real/value/"; # ..更多代码在这里.. 1个;

    现在在你的单元测试中:

    使用我的::类; *{My::Class::BASE_PATH} = sub { "/a/fake/value" }; # 在这里做你的测试...

    【讨论】:

    • 这可以在模块中使用 subs 吗?例如:use myModule; *{myModule} = sub { "/a/fake/value" }; 如果没有,有没有办法做到这一点?
    • @GuySoft 看我的回答
    • @EvanCarroll 我的问题是关于模块中的 subs,在非常规文档中看不到。同样在我编辑代码的时候,我无法真正编辑。无论如何,这是 6 年前的事了,我无法访问该代码库
    • @GuySoft 只是为了直接回答这个问题,模块中的 subs 可以使用 Ether 提供的语法从任何包的 perl 中重新定义。那是除非这些子是内联的。然后在您重新定义它们时为时已晚。像constant 这样的一些模块会产生内联的子程序。对于那些你需要更多的人,比如unconstant
    【解决方案4】:

    测试通常会显示设计中的不灵活。这是其中之一。这个常数不应该是常数。

    如果您出于性能原因这样做,我愿意用硬通货打赌它没有任何区别。

    【讨论】:

    • 我认为向开发人员传达常量在程序执行过程中不会改变并且仍然允许测试人员改变它是有用的。这很常见。例如,在 C 语言中,您可以使用预处理器使用自定义逻辑设置一个常量。我没有看到关于预优化的内容。他想说基本路径是不可变的,不能改变,除非他想在测试环境中改变它。
    • @EvanCarroll 更多的是关于不灵活。如果您需要在测试中更改它,您可能还有其他原因;尤其是一条路径。为了传达其目的,请将其包装在配置对象中。它可以从一个简单的散列包装器开始,并以此为基础:从配置文件、环境变量中读取,或者为开发、测试和生产选择合适的值。
    【解决方案5】:

    unconstant

    一个简单的方法是使用unconstant

    # From ./lib/Foo.pm
    package Foo {
        use constant BASE_PATH => '/data/monitor/';
    }
    
    
    # From test.pl
    use unconstant;  # Do this first
    use Foo;
    use constant *Foo::BASE_PATH => '/otherData/otherMonitor/';
    

    免责声明:我是unconstant 的作者,但我遇到了同样的问题。

    【讨论】:

      猜你喜欢
      • 2015-03-30
      • 2015-06-07
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 2020-05-25
      相关资源
      最近更新 更多