Python - 模拟链式函数调用

Pra*_*eep 9 python mocking chained

我在单元测试下的一个方法中有以下声明.

db_employees = self.db._session.query(Employee).filter(Employee.dept ==   
    new_employee.dept).all()
Run Code Online (Sandbox Code Playgroud)

我希望db_employees获得模拟员工列表.我尝试使用以下方法实现此目的

 m = MagickMock()
 m.return_value.filter().all().return_value = employees
Run Code Online (Sandbox Code Playgroud)

员工是员工对象的列表.但这没效果.当我尝试打印任何属性的值时,它具有模拟值.这是代码的样子:

class Database(object):
    def __init__(self, user=None, passwd=None, db="sqlite:////tmp/emp.db"):
        try:
            engine = create_engine(db)
        except Exception:
            raise ValueError("Database '%s' does not exist." % db)

        def on_connect(conn, record):
            conn.execute('pragma foreign_keys=ON')

        if 'sqlite://' in db:
            event.listen(engine, 'connect', on_connect)
        Base.metadata.bind = engine
        DBSession = sessionmaker(bind=engine)
        self._session = DBSession()


class TestEmployee(MyEmployee):
    def setUp(self):
        self.db = emp.database.Database(db=options.connection)
        self.db._session._autoflush()

    @mock.patch.object(session.Session, 'add')     
    @mock.patch.object(session.Session, 'query')  
    def test_update(self, mock_query, mock_add):
        employees = [{'id': 1,
                      'name': 'Pradeep',
                      'department': 'IT',
                      'manager': 'John'}]
        mock_add.side_effect = self.add_side_effect
        mock_query.return_value = self.query_results()  
        self.update_employees(employees)

    def add_side_effect(self, instance, _warn=True):
        // Code to mock add
        // Values will be stored in a dict which will be used to 
        // check with expected value.

    def query_results(self):  
        m = MagicMock()  
        if self.count == 0:  
             m.return_value.filter.return_value.all.return_value = [employee]  
        elif:  
             m.return_value.filter.return_value.all.return_value = [department]  
        return m  
Run Code Online (Sandbox Code Playgroud)

我有query_results作为测试调用查询两次的方法.首先是员工表,然后是部门表.

如何模拟此链接函数调用?

Tat*_*ton 14

m = MagickMock()
m.session.query().filter().all.return_value = employees
Run Code Online (Sandbox Code Playgroud)

https://docs.python.org/3/library/unittest.mock.html

  • 如何解决“预期‘过滤器’已被调用一次”的问题。在执行诸如“m.session.query.assert_used_once()”之类的操作时调用了 2 次。 (2认同)

Ron*_*ren 7

我找到了一个类似问题的解决方案,我需要模拟一组嵌套的过滤调用。

给定的测试代码类似于以下内容:

interesting_cats = (session.query(Cats)
                           .filter(Cat.fur_type == 'furry')
                           .filter(Cat.voice == 'meowrific')
                           .filter(Cat.color == 'orande')
                           .all())
Run Code Online (Sandbox Code Playgroud)

您可以设置模拟如下:

mock_session_response = MagicMock()
# This is the magic - create a mock loop
mock_session_response.filter.return_value = mock_session_response
# We can exit the loop with a call to 'all'
mock_session_response.all.return_value = provided_cats

mock_session = MagicMock(spec=Session)
mock_session.query.return_value = mock_session_response
Run Code Online (Sandbox Code Playgroud)


Mic*_*ico 0

您应该修补的属性query()方法并对其进行配置,以便为您提供正确的答案。您可以通过多种方式完成此操作,但恕我直言,更干净的方法是修补的静态引用。我不知道您导入的女巫模块,因此我将修补本地参考。_sessionDatabaseDBSessionqueryDBSession

另一方面是模拟配置:我们将设置query的返回值,在您的情况下,该返回值将成为具有方法的对象filter()

class TestEmployee(MyEmployee):
    def setUp(self):
        self.db = emp.database.Database(db=options.connection)
        self.db._session._autoflush()
        self.log_add = {}

    @mock.patch.object(__name__.'DBSession.add')     
    @mock.patch.object(__name__.'DBSession.query')  
    def test_update(self, mock_query, mock_add):
        employees = [{'id': 1,
                      'name': 'Pradeep',
                      'department': 'IT',
                      'manager': 'John'}]
        mock_add.side_effect = self.add_side_effect
        mock_query.return_value = self.query_results()  
        self.update_employees(employees)
        .... your test here

    def add_side_effect(self, instance, _warn=True):
        # ... storing data
        self.log_add[...] = [...]

    def query_results(self):  
        m = MagicMock()
        value = "[department]"
        if not self.count:  
             value = "[employee]"  
        m.filter.return_value.all.return_value = value 
        return m
Run Code Online (Sandbox Code Playgroud)