Patching datetime
00:00
In the previous lesson I showed you how to use the patch object mechanism. In this lesson, I’ll be showing you one of the most common uses of patch and mocks: faking out dates and times in tests.
00:12 If the behavior of your code changes based on the current date or time, then your tests need to fake out the date or time to properly test your code. I’m not going to teach you anything new in this lesson.
00:23
You’ve already seen all the techniques you need in order to patch the datetime module, but this use case is pretty common, so it’s worth showing explicitly.
00:32
I’d guess something like 90% of the time I’m using Mock, I’m mocking a date or time. Like you’ve seen before, the trick to getting it right is to patch the spot where the object gets used.
00:43
You won’t be patching the datetime in the test code, but patching the datetime in the code that your test is invoking. To make it just a tiny bit more confusing, you often also have to import the real datetime into your test code in order to have the necessary tools needed to manipulate your dates and check if your code works.
01:03 Let’s go look at an example.
01:06
This is spooky.py, which has a single function, is_spooky(). What this function does is look at the current date, and if it’s a Friday the 13th, it returns True; otherwise it returns False.
01:20
In case you’re not familiar with datetime objects, the .day attribute is the day of the month, so 13 in our case. Well, the .weekday() function returns a number from 0 to 6 for the day of the week, with 0 being Monday, so 4 here means Friday.
01:36
Do note that there is also an .isoweekday(), which returns a number from 1 to 7 instead, because, well, dates are messy. In the window below, I’ve got some test code.
01:49
Because the code is going to compare datetime objects, I need to import the real datetime into the test code. Then, of course, I import the thing that I’m testing, is_spooky().
02:00
And this is the tricky part. I’m patching datetime inside of this spooky module. So now within the test code, I’m referring to two kinds of datetime: the real one, which I imported, and the mocked one from the spooky module, which inside of this context block is known as mock_dt. To ensure that is_spooky() is doing its job, I want to fake out a Friday the 13th by setting the return value of .now() on my mock_dt.
02:30
Then I call is_spooky() and check that it returns True.
02:35
This next test is a negative one: setting the date to Saturday the 14th, I should get back False from is_spooky(). Saturday the 14th, by the way, is a very silly mock horror movie from the 1980s.
02:47 Worth checking out if you’re in a strange mood.
02:50
Because I know how is_spooky() works, I’m also going to do a negative test that is a Friday, but not a 13th using the week before.
02:59
I’m not going to bother running this code, as you’ve seen plenty of “ran 1 test, OK” messages by this point, but hopefully you get the idea. One note of caution: you already know that the patch location has to be the point of use, but with datetime, you should also be aware of how datetime is being used. Because the module and the class inside of it have the same name, you need to know which thing you are patching.
03:23
Here I patched the class, because that’s what spooky.py imported. Depending on the code that you are testing, you might be patching the module instead.
03:32
That’s not drastically different, it just means you have one more layer in the call. So mock_dt.datetime.now() instead of just mock_dt .now. One of the wonderful things about a mock object is you can call any method on it and it works.
03:50 One of the horrible things about a mock object is you can call any method on it and it works. Next up, specifications.
Become a Member to join the conversation.
