views:

156

answers:

1

I need to write a unit test for a function that returns a dictionary. One of the values in this dictionary is datetime.datetime.now() which of course changes with every test run.

I want to ignore that key completely in my assert. Right now I have a dictionary comparison function but I really want to use assertEqual like this:

def my_func(self):
    return {'monkey_head_count': 3, 'monkey_creation': datetime.datetime.now()}

... unit tests

class MonkeyTester(unittest.TestCase):
    def test_myfunc(self):
        self.assertEqual(my_func(), {'monkey_head_count': 3}) # I want to ignore the timestamp!

Is there any best practices or elegant solutions for doing this? I am aware of assertAlmostEqual(), but that's only useful for floats iirc.

+6  A: 

Just delete the timestamp from the dict before doing the comparison:

class MonkeyTester(unittest.TestCase):
    def test_myfunc(self):
        without_timestamp = my_func()
        del without_timestamp["monkey_creation"]
        self.assertEqual(without_timestamp, {'monkey_head_count': 3})

If you find yourself doing a lot of time-related tests that involve datetime.now() then you can monkeypatch the datetime class for your unit tests. Consider this

import datetime
constant_now = datetime.datetime(2009,8,7,6,5,4)
old_datetime_class = datetime.datetime
class new_datetime(datetime.datetime):
    @staticmethod
    def now():
        return constant_now

datetime.datetime = new_datetime

Now whenever you call datetime.datetime.now() in your unit tests, it'll always return the constant_now timestamp. And if you want/need to switch back to the original datetime.datetime.now() then you can simple say

datetime.datetime = old_datetime_class

and things will be back to normal. This sort of thing can be useful, though in the simple example you gave, I'd recommend just deleting the timestamp from the dict before comparing.

Eli Courtwright
Why didn't I think of that simple solution? Although I will be testing a lot of possible return values of my_func, so I think I will monkeypatch datetime as you suggested. Much appriciated, really clever!
pojo
You should put code to switch back the datetime in a teardown method -- that way this unit test won't affect other test classes
Kathy Van Stone
Another option is to pass in datetime.datetime.now as the default for a named parameter; pass in your own function in the unit test. This avoids monkeypatching.
chrispy