Mock SQLAlchemy scoped_session query and Why Python is My BFF

sqlAlchemy has sessionmaker to create a session from which you can use query to get whatever you need. For example:

someSession.query(SomeTableRepresentationObject).filter...ect

If you’ve used sqlalchemy, nothing new going on here but it wouldn’t be me if I didn’t point out the obvious and take three sentences to do so.

Now what you may run into is something like this:

def getCountOfUsersByUserName(userName, session):
  return session.query(User).filter(User.userName == userName).count()

Ok now that I typed that out, I see it’s kind of a dumb method. But f–k it, what’s done is done.

Now from a testing situation, this could look tough. After all if you come from a more static language background like I did, you should know how hard things can be to mock at times. (.Net Session… I’m looking at you with judging eyes. Sooooo judging.) But this isn’t a static language, it’s Python.

For purposes of making things less confusing, which is hard for me, when I use the word “Object” I mean method OR class. And remember as always, “banana” is the safe word.

You see, to mock that out all you need is an object that has a query object that takes in something and a filter object on the query object that takes in something and has a count method on it. Yeah that totally makes sense. Try working it backward.

Filter has one parameter and a count method on it. Whatever that parameter is, it doesn’t matter since it is Python. As long as Filter takes in one parameter, you have a winner.

Query, like Filter, takes in one parameter and has a object named Filter on it. Once again, it doesn’t matter what’s passed into Query, just that it takes something in.

Session is basically an object that has no parameters and has a Query object on it.

Now in a static language, this would be annoying. You would need 3 interfaces and mock a whole ton of stuff dynamically with something like Rhino Mocks or just create a bunch of classes of those interfaces that you can pass in. Either way, there are complications. Once you get into things like Web Session or ViewState, it’s a ton of work. Python? Eh, you can do it in 20ish lines. How you ask? Well I’ll show you how!

	class mockFilter(object): #This is the ONE parameter constructor
		def __init__(self):
			self._count = 0
			self._first = dynamicObject()

		def first(self):  #This is the another method that's just coming along for the ride.
			return self._first

		def count(self):  #This is the needed Count method
			return self._count

	class mockQuery(object): #This is the ONE parameter constructor
		def __init__(self):
			self._filter = mockFilter()

		def filter(self, placeHolder): #This is used to mimic the query.filter() call
			return self._filter 

	class mockSession(object):
		def __init__(self):
			self._query = mockQuery()
			self.dirty = []

		def flush(self):
			pass

		def query(self, placeHolder):  #This is used to mimic the session.query call
			return self._query

  #and this... THIS IS SPARTA!!1111... yeah I know, I'm about 3 years too late on that joke.

How does this work? Say I have the method from above:

def getCountOfUsersByUserName(userName, session):
  return session.query(User).filter(User.userName == userName).count()

I could test it using this:

  session = mockSession()
  session.query('').filter('')._count = 0 #Initialize the mock session so it returns 0 from count()

  getCountOfUsersByUserName('sadas', session)

And boom, you have mocking in ten minutes or less or your code is free.

As you can see, the highly dynamic nature of Python makes it a great fit for any project that will need unit tests and mocking. And which project doesn’t? You know what I’m sayin’? You now what I’m saying’? High five!

Note: dynamicObject is …. nothing but a cheesy class that inherits Object but has nothing on it. (Turns out that if I did someObject = Object() I couldn’t do this since Object by default doesn’t contain the ability to add things dynamically… and this was by design.)

And yes, I just quoted myself. I am that awesome.