How to check a function using DateTime to get the current time?

Most of the answers I saw in StackOverflow do not use the DateTime object and instead use the date() function. This makes them very dirty decisions (redefinition of date() , mockery of the protected function of the subject, etc.).

Is there a way to mock DateTime by effectively mocking the current date / time?

As an example, here is the code I would like to test:

 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(); } 

Here I set the date and time in the CommandHistory class:

 class CommandHistory { // Property definitions... public function __construct() { $this->time = new \DateTime(); } } 

And here is my unit test:

 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; } 

As you can see, I created a rather long closure just to exclude comparing the field that contains the current time, and I feel that this affects the readability of my test.

Also, in order to maintain ease of use for people who use this class, I do not want them to switch to the create() function at the current time. I believe that adding weird behavior to my classes just to make them testable means that I'm doing something wrong.

+5
source share
1 answer

Thus, the standard approach to solving this issue is based on the assumption that in your current implementation you have a static, implicit, undeclared dependence on the object that provides the current time (wrapped in a new instance of the DateTime object). If you did this with your own code (and not with a class from the framework / language), you too will not be able to easily test.

The solution is to stop using the implicit undeclared dependency and explicitly declare your implicit dependency. I would do this by creating a DateTimeProvider (or DateTimeFactory ) interface that has a GetCurrentDateTime method. Pass this to your constructor for your CommandHistoryCreator and pass it to the CommandHistory constructor. CommandHistory then ask the provider to get the current time object, rather than creating a new one itself, and can continue to work as it is.

This will allow you to set the DateTime layout in your tests and verify that the CommandHistory saved with the correct DateTime

+10
source

All Articles