问题
Say I have two models set up like so:
class ParentFoo(SqlBase):
__tablename__ = "parents"
id = Column(String, primary_key=True)
children = relationship("ChildFoo", backref="parents")
class ChildFoo(SqlBase):
__tablename__ = "children"
id = Column(String, primary_key=True)
Now in a request handler, say I want to get all children entities from the database given a parent:
class RequestHandler():
def get(self, parent_id):
parent = database.query(parent_id)
children = parent.children
send_response(children)
The SQLAlchemy ORM API allows this type of query. The problem for me is when I'm writing my tests. I have a generic RequestHandler class that takes a ParentFoo and a ChildFoo class as arguments in the constructor. So my request handler class actually looks like this:
class GenericRequestHandler():
def __init__(self, parent_class, child_class):
self.parent_class = parent_class
self.child_class = child_class
def get(self, parent_id):
parent = database.query(self.parent_class, parent_id)
children = parent.getattr(self.child_class.__tablename__, None)
As long as the column name on the parent matches the __tablename__ for the child entities, this will work. I enforce this convention. The problem is testing this.
Python's mock library doesn't seem to provide for return values on attribute lookups and I don't want to mock out the getattr function, because it's used elsewhere in my get method.
Any thoughts?
Edit 1: Regarding my comment about attribute lookups: I can mock out a function and provide a return value.
mocked_function.return_value = my_desired_value
But how do I mock out an attribute?
mocked_object.attribute_of_interest = my_desired_value
Edit 2:
In my tests, I provide MagicMock objects to the constructor for the parent and child classes, and I set a MagicMock object to be returned by the call to database.query:
parent_class_mock = MagicMock()
child_class_mock = MagicMock()
parent_entity = MagicMock()
type(parent_entity).getattr = PropertyMock(return_value=[child_entity])
database.query.return_value = parent_entity
class ConcreteRequestHandler(GenericRequestHandler):
def __init__(self, parent_class, child_class):
super(ConcreteRequestHandler, self).__init__(parent_class, child_class)
As you can see, I tried to use mock's PropertyMock to set an attribute. When I ran the test, I got an error saying 'list' object not callable. Please see this SO question I asked related to this.
来源:https://stackoverflow.com/questions/36782736/testing-return-value-of-sqlalchemy-parent-child-relationship-query