How to test DateTimeProperty in App Engine NDB?
Asked Answered
S

1

7

I'm trying to test a filter for the DateTimeProperty with App Engine's NDB but I have it set to auto_now?

Is there a way to get around this for unit testing?

Example:

class MyModel(ndb.Model)
  timestamp = ndb.DateTimeProperty(auto_now)
  name = ndb.StringProperty()


def testMyModelFilter(self):
  test1 = MyModel()
  test1.timestamp = datetime.datetime.now() - datetime.timedelta(hours=2)
  test1.put()
  test2 = MyModel()
  test2.timestamp = datetime.datetime.now() - datetime.timedelta(hours=1)
  test2.put()

  hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
  fetched = MyModel.query().filter(MyModel.timestamp < hour_ago).fetch(
      None, keys_only=True)

Unfortunately, when I commit it to the datastore with test.put(), it uses the time when it was put().

Suited answered 23/10, 2012 at 6:55 Comment(3)
Some ideas: override _pre_put_hook() or set MyModel.timestamp to a normal ndb.DateTimeProperty if you're in a test.Leanoraleant
Is the main issue that since it is recording a different timestamp, and you are looking for a way to filter on that exact time to verify that your filter works? In other words, would it suffice to assign the result of test.put() to test_key, and then get the entity using the key?Coleen
@Coleen yes, I'm looking to verify my filter. I've expanded my example to include the lookup.Suited
C
10

So one thing you can try (as alluded to by @mjibson) is overriding your model during the test. Since MyModel itself is an object, you can modify the _auto_now property of timestamp to False for your test. For example:

def testMyModelFilter(self):
  # Change the auto_now parameter to False
  MyModel.timestamp._auto_now = False

  # Test as usual...
  test1 = MyModel()
  test1.timestamp = datetime.datetime.now() - datetime.timedelta(hours=2)
  test1.put()
  test2 = MyModel()
  test2.timestamp = datetime.datetime.now() - datetime.timedelta(hours=1)
  test2.put()

  hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
  fetched = MyModel.query().filter(MyModel.timestamp < hour_ago).fetch(
      None, keys_only=True)

I remember seeing this technique somewhere else, so I'll link if I can find it (if it works, of course :) ).

Coleen answered 23/10, 2012 at 8:5 Comment(5)
Do I have to use dict or can I override the parameter? MyModel.timestamp = ndb.DateTimeProperty(auto_now=False)Suited
@Suited I could be wrong (and it is nearing my bedtime, so I probably am), but I think that will mess with the manner in which the model properties function (GAE models use descriptor classes, which have their own peculiarities behind them). However you did bring to light a slightly better version which I'll post now :)Coleen
@Suited More specifically, if you compare dir(MyModel.timestamp) before you try reassigning ndb.DateTimeProperty a la your previous comment, to the value after your assign it, you'll see that it is missing the model_class parameter, which is where your MyModel class reference is stored.Coleen
While this should work, it still fails in my test. My current work around i just to not use the auto_now option and manually set it when create the entity with a class method. Should it affect the test if they're in 2 separate files/modules (models.py and models_test.py)?Suited
@Suited Okay, one more thing. I forgot that NDB models had a different underlying structure (which should have been obvious, my bad) - try using MyModel.timestamp._auto_now = False (auto_now is now a private variable). I just ran this locally with some simple tests that mimic your structure and it seems to work, so let me know if it is still off and we can find a way to tweak it.Coleen

© 2022 - 2024 — McMap. All rights reserved.