基于参数模拟 sqlalchemy 查询。例如:db.session.query(Model.id)

时间:2021-07-30 07:52:12

标签: python sqlalchemy mocking flask-sqlalchemy python-unittest

我带有模型的 sqlalchemy 应用

## example file  
## models.py -- path : app/models.py
db = SQLAlchemy(app)
class User(db.Model):
  
    __tablename__ = 'User'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
class Book(db.Model):
    __tablename__ = 'Book'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True, nullable=False)
    owned_by = = db.Column(BigInt, db.ForeignKey('User.id'))


## i want to mock `user` and `book` returns by query
def check_user_own_book(user_id):
    user = db.query.session(
            User.id,
            User.username,
    ).filter(
            User.id == user_id
    ).one_or_none()
    ## more logic. abc 

    book = db.query.session(
            Book.id,
            Book.name,
    ).filter(
            owned_by == user.id
    ).one_or_none()

     ## more logic
            

这是我的测试

from unittest import MagicMock, mock
def test_mock_session_query(fixture_user, fixture_book, session ):
    # fixture_user , fixture_book are fixture object 
    # session is inited in confest.py
    with mock.patch('flask_sqlalchemy._QueryProperty.__get__') as mock_model:
        mock_model.filter.return_value.one_or_none.return_value = fixture_user 


        ## let test 


        # query_user_with_session return None (which is true because there is no such user with id == 1000) 
        # -> Why do mock_model does not work and how to mock this `session.query` (1)
        query_user_with_session = session.query(User.id, User.username).filter(User.id==1000).one_or_none() 
         

        # query_user_with_class returns fixture_user correctly with what we mocked
        query_user_with_class =  User.query.filter(User.id==1).one_or_none() 
        # query_book_with_class returns fixture_user (2)
        query_user_with_session = Book.query.filter(Book.id=10000).one_or_none()
        

        has_own_book = check_user_own_book(user_id=1) # this will fail because all query with one_or_none will return fixture_user

我为此研究了几天,但仍然没有任何答案:

  1. Model.querysession.query(Model) 的行为不同。那么mock后面那个的技巧是什么

  2. 如何根据参数模拟 return_valuesession.query(Model.column1, Model.columns2)。例如:session.query(Model.column1, Model.columns2)session.query(Model_2.column1, Model_2.columns2) 应该返回不同的

1 个答案:

答案 0 :(得分:1)

不确定模拟 session.query() 是个好主意。它是为了什么?您可以立即模拟查询结果 (Result.one_or_none())。我也可以推荐使用 side_effect。这真的很容易、易懂且有用。下面是一个例子:

# somewhere in your code
def get_user_and_book():
    user = db.session.query(...).filter(...).one_or_none()
    book = db.session.query(...).filter(...).one_or_none()
    return user, book

# test
class Example(unittest.TestCase):
    def test_one_or_none(self):
        with mock.patch('sqlalchemy.engine.result.Result.one_or_none', side_effect=(1, 2, 3, 4,)):
            # get_user_and_book() calls one_or_none() 2 times
            # so we'll expects 1, 2 - see side_effect
            a, b = get_user_and_book()
            self.assertEqual(1, a)
            self.assertEqual(2, b)
            # let's try one more time
            c, d = get_user_and_book()
            self.assertEqual(3, c)
            self.assertEqual(4, d)

通过这种方式,您可以为所有可能的组合(用户找到 + 未找到书籍、用户找到 + 书籍找到等)编写测试