我有一个Web应用程序,我想要运行一些系统测试,为了做到这一点,我将需要移动系统时间。该应用程序一直使用DateTime。
有没有人对如何更改DateTime->现在报告的时间有任何建议?我唯一想到的是继承DateTime并搞乱所有'使用'线,但这看起来很有侵略性。
答案说明:
这三个都可以正常工作,但Hook :: LexWrap是我选择的那个因为(a)我想移动时钟而不是稍微摇晃它(这更像是Time ::的目的::模拟和朋友呢); (b)我一直使用DateTime,如果我不小心不使用它,我很高兴出现错误; (c)Hook :: LexWrap比符号表中的hack更优雅,因为它做同样的事情。 (另外,它原来是我已安装的某些模块的依赖项,所以我甚至不用CPAN它...)
答案 0 :(得分:11)
除了采用高级方法并专门包装DateTime
之外,您可能需要查看模块Test::MockTime和Time::Mock,它们会覆盖{{{}}的低级函数。 1}}等使用,并且(运气好的话)会在任何时间敏感的代码上做正确的事情。对我而言,它似乎是一种更强大的测试方式。
答案 1 :(得分:4)
我认为Hook :: 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对当前时间的概念,您可以随时调整$offset
。
$offset
的计算应该比上面显示的更复杂。例如,要将“当前时间”设置为绝对时间:
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更改之类的内容会导致时间跳错到错误的伪时间。
答案 2 :(得分:1)
您可以通过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";
}
答案 3 :(得分:1)
在设计具有可测试性的新类时,理想的解决方案是能够注入新的日期对象。
但是,对于使用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