代码之家  ›  专栏  ›  技术社区  ›  ijw

如何使用不断变化的系统时间测试Perl应用程序?

  •  2
  • ijw  · 技术社区  · 15 年前

    我有一个Web应用程序,我想运行一些系统测试,为了做到这一点,我需要移动系统时间。应用程序一直使用datetime。

    是否有人对如何更改datetime->现在报告的时间有任何建议?唯一想到的是对datetime进行子类化,并对所有的“use”行进行混乱处理,但这似乎有点入侵性。

    答案说明:

    这三种方法都可以,但hook::lexwrap one是我选择的方法,因为(a)我想移动时钟而不是稍微晃动它(这更像是time::mock和friends所做的);(b)我一直使用datetime,如果我不小心出错了,我很高兴出现错误。 使用它;(c)hook::lexwrap比符号表中的hack更优雅,因为它所做的一切都是相同的。(而且,它是我已经安装的某个模块的依赖项,所以我甚至不必对它进行CPAN…)

    4 回复  |  直到 12 年前
        1
  •  1
  •   daotoad    15 年前

    您可以通过使用代码注入 Hook::LexWrap 截取now()方法。

    use Hook::LexWrap;
    
    use DateTime;
    
    # Use real now
    test();
    
    {
        my $wrapper = wrap 'DateTime::now',
            post => sub {
                $_[-1] = DateTime->from_epoch( epoch => 0 );
            };
    
        # Use fake now
        test();
    
    }
    
    # use real now again
    test();
    
    sub test {
        my $now = DateTime->now;
    
        print "The time is $now\n";
    }
    
        2
  •  11
  •   hobbs    15 年前

    而不是采取高层次的方法和包装 DateTime 具体地说,您可能希望研究模块 Test::MockTime Time::Mock ,它将覆盖 日期时间 等等。利用,并且(运气好的话)会做正确的事情 任何 时间敏感代码。对我来说,这似乎是一种更强有力的测试方法。

        3
  •  4
  •   cjm    15 年前

    我认为在这种情况下,lexwrap是多余的。重新定义这样一个简单的函数更容易。

    use DateTime;
    
    my $offset;
    
    BEGIN {
      $offset = 24 * 60 * 60; # Pretend it's tomorrow
    
      no warnings 'redefine';
    
      sub DateTime::now
      {
        shift->from_epoch( epoch => ($offset + scalar time), @_ )
      }
    } # end BEGIN
    

    你可以代替 my $offset 具有 our $offset 如果您需要访问 $offset 从包含此代码的文件外部。

    你可以调整 美元偏移 在任何时候,如果您想在运行期间更改datetime对当前时间的想法。

    计算 美元偏移 可能比上面显示的更复杂。例如,要将“当前时间”设置为绝对时间:

    my $want = DateTime->new(
       year   => 2009,
       month  => 9,
       day    => 14,
       hour   => 12,
       minute => 0,
       second => 0,
       time_zone => 'America/Chicago',
    );
    
    my $current = DateTime->from_epoch(epoch => scalar time);
    
    $offset = $want->subtract_datetime_absolute($current)->in_units('seconds');
    

    但是您可能需要计算一个固定的秒数来添加到当前时间中,这样时间就会在这之后正常前进。使用的问题 add( days => 1 ); 在重新定义的 now 方法是,像DST更改这样的事情会导致时间跳到错误的伪时间。

        4
  •  1
  •   Unk    12 年前

    在设计一个具有可测试性的新类时,理想的解决方案是能够注入新的日期对象。

    但是,对于现有代码,使用 DateTime->now DateTime->today 下面是一个可能的、范围适当的解决方案。我在这里将它作为一种实现这一点的方法,而不将hook::lexwrap作为依赖项引入,也不影响全局行为。

    {
        no strict 'refs';
        no warnings 'redefine';
        local *{'DateTime::today'} = sub {
            return DateTime->new(
                year  => 2012,
                month => 5,
                day   => 31
            );
        };
        say DateTime->today->ymd(); # 2012-05-31
    };
    
    say DateTime->today->ymd(); # today