【问题标题】:Mock sqlalchemy query based on the arguments .E.g: db.session.query(Model.id)基于参数的模拟 sqlalchemy 查询。例如:db.session.query(Model.id)
【发布时间】:2021-07-30 07:52:12
【问题描述】:

我使用模型的 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) 的行为不同。那么模拟后一个的技术是什么

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

【问题讨论】:

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


    【解决方案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)
    

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

    【讨论】:

    • 感谢您的评论。我试过你的例子,但我得到了ModuleNotFoundError: No module named 'sqlalchemy.engine.result.Result'; 'sqlalchemy.engine.result' is not a package,模块可以修补吗?
    • @BaoTran 什么sqlalchemy版本?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 2015-05-26
    相关资源
    最近更新 更多