如何测试使用DateTime获取当前时间的函数?

Par*_*dar 5 datetime phpunit unit-testing symfony mockery

我在StackOverflow上看到的大多数答案都没有使用该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();
}
Run Code Online (Sandbox Code Playgroud)

这是我在CommandHistory课堂上设置日期和时间的地方:

class CommandHistory
{
    // Property definitions...

    public function __construct()
    {
        $this->time = new \DateTime();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的单元测试:

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;
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我创建了一个相当冗长的闭包只是为了排除包含当前时间的字段的比较,我觉得这会损害我的测试的可读性.

此外,为了保持使用此类的人的易用性,我不想让它们在当前时间传递给该create()函数.我认为在我的类中添加奇怪的行为只是为了使它们可测试意味着我做错了.

Sam*_*der 11

因此,解决此问题的标准方法依赖于接受在当前实现中对对象提供静态,隐式,未声明的依赖关系,该对象提供当前时间(包含在DateTime对象的新实例中).如果您使用自己的代码(而不是框架/语言中的类)执行此操作,您将无法轻松地进行测试.

解决方案是停止使用隐式未声明的依赖关系并明确声明隐式依赖关系.我会通过创建一个具有方法的DateTimeProvider(或DateTimeFactory)接口来完成此操作GetCurrentDateTime.将此传递给您的构造函数,CommandHistoryCreator并将其传递给CommandHistory构造函数.然后,CommandHistory将要求提供者获取当前日期时间对象,而不是自己创建一个新对象,并且可以继续保持原样.

这将允许您DateTime在测试中提供模拟并检查它CommandHistory是否保持正确DateTime

  • 你问了一个解决方案并得到了一个好的解决方案.告诉那些开发人员测试他们的代码,他们也需要这个...也许由于缺乏良好的代码测试,这是不熟悉的. (4认同)