如何测试使用 DateTime 获取当前时间的函数?
Posted
技术标签:
【中文标题】如何测试使用 DateTime 获取当前时间的函数?【英文标题】:How can I test a function that uses DateTime to get the current time? 【发布时间】:2015-07-20 03:30:11 【问题描述】:我在 *** 上看到的大多数答案都没有使用 DateTime
对象,而是使用了 date()
函数。这使得它们的解决方案非常肮脏(覆盖date()
、模拟被测对象的受保护函数等)。
有没有办法模拟DateTime
,有效地模拟当前日期/时间?
例如,这是我要测试的代码:
public function __construct(UserInterface $user, EntityManager $manager)
$this->user = $user;
$this->manager = $manager;
public function create(Tunnel $tunnel, $chain, $response)
$history = new CommandHistory();
$history->setTunnel($tunnel)
->setCommand($chain)
->setResponse($response)
->setUser($this->user)
;
$this->manager->persist($history);
$this->manager->flush();
这是我在CommandHistory
班级中设置日期和时间的地方:
class CommandHistory
// Property definitions...
public function __construct()
$this->time = new \DateTime();
这是我的单元测试:
public function testCreate()
$user = new User();
$manager = $this->mockManagerWithUser($user);
$tunnel = $this->tunnel;
$chain = 'Commands`Chain';
$response = 'This is the response!';
$creator = new CommandHistoryCreator($user, $manager);
$creator->create($tunnel, $chain, $response);
protected function mockManagerWithUser(UserInterface $user)
$manager = \Mockery::mock('Doctrine\ORM\EntityManager');
$manager->shouldReceive('persist')->once()->with(\Mockery::on(function(CommandHistory $argument) use ($user)
return
$argument->getCommand() === 'Commands`Chain'
&& $argument->getResponse() === 'This is the response!'
&& $argument->getTunnel() === $this->tunnel
&& $argument->getUser() === $user
;
));
$manager->shouldReceive('flush')->once()->withNoArgs();
return $manager;
如您所见,我创建了一个相当冗长的闭包,只是为了排除包含当前时间的字段的比较,我觉得这会损害我的测试的可读性。
另外,为了让使用这个类的人保持易用性,我不想让他们在当前时间传递给create()
函数。我相信在我的类中添加奇怪的行为只是为了让它们可测试意味着我做错了。
【问题讨论】:
您在哪里使用当前时间?我对 php 不太熟悉,但我看不到任何地方使用的日期或时间是上面的任何代码...我假设它是 CommandHistory 对象的属性,您没有显示...跨度> 是的,在CommandHistory
的构造函数中。我刚刚也将其添加到我的问题中。谢谢提醒:-)
【参考方案1】:
因此,解决此问题的标准方法依赖于接受以下事实:在您当前的实现中,您对提供当前时间的对象(包装在 DateTime 对象的新实例中)具有静态、隐式、未声明的依赖关系。如果您使用自己的代码(而不是框架/语言中的类)执行此操作,您也将无法轻松进行测试。
解决方案是停止使用隐式未声明的依赖,并显式声明你的隐式依赖。我会通过创建一个具有GetCurrentDateTime
方法的DateTimeProvider
(或DateTimeFactory
)接口来做到这一点。将此传递到您的 CommandHistoryCreator
的构造函数中,并将其传递到 CommandHistory
构造函数中。然后CommandHistory
将要求提供者获取当前日期时间对象,而不是自己创建一个新对象,并且可以照原样继续。
这将允许您在测试中提供模拟 DateTime
并检查 CommandHistory
是否与正确的 DateTime
保持一致
【讨论】:
好吧,这种方法的问题在于,使这种依赖关系显式化并不能为其他开发人员解决任何问题。他们现在必须做一些在 PHP 领域非常陌生的事情,即将DateTimeFactory
传递给 PHP 实体的构造函数。
您要求解决方案并得到一个好的解决方案。告诉那些开发人员测试他们的代码,他们也会需要这个……也许这是由于缺乏良好的代码测试而感到陌生。
@parhamdoustdar 这就是为什么我以“依赖于接受在您当前的实现中具有静态、隐式、未声明的依赖关系”开头的原因。在大多数语言中,日期时间都是这样的,在大多数语言中,人们不习惯提供工厂“只是为了获取当前时间”。如果你让构造函数需要接口,人们将能够看到他们需要它并且他们会习惯使用它。以上是关于如何测试使用 DateTime 获取当前时间的函数?的主要内容,如果未能解决你的问题,请参考以下文章