pythonpytest

How can I test that a method of a class was passed as a parameter to a mocked method


Say I have classes like this:

class MyTask(QtCore.QObject):
    def do(self):
        ...

class Manager(QtCore.QObject):
    def __init__(self):
        super().__init__()
        self.worker_executor = concurrent.futures.ThreadPoolExecutor()

    def run_task(self):
        self.worker_executor.submit(MyTask().do)

I want to test, using pytest, that method do of an (i.e. any) instance of MyTask is passed as the parameter of ThreadPoolExecutor.submit.

manager.worker_executor.submit = mock.Mock()
manager.run_task()
mock_call = manager.worker_executor.submit.call_args_list[0]
assert mock_call.args[0] == MyTask.do # False

I'm not surprised this fails, with a message like:

AssertionError: assert <bound method MyTask.do of <MyTask object at 0x000001B07E6537F0>> == <function MyTask.do at 0x000001B07D2E3BE0>

as a bound method is being compared with a function.

Equivalent to How to verify that specific class method was passed as parameter?, but that relates to Java and a Java mocking framework.


Solution

  • As suggested in my comment,

    assert mock_call.args[0].__func__ == MyTask.do
    # True, both are <function __main__.MyTask.do(self)>
    

    Also works with subclasses of MyTask that do not override the do function:

    class OtherTask(MyTask):
        ...
    
    task = OtherTask()
    assert task.do.__func__ == MyTask.do
    # True, both are <function __main__.MyTask.do(self)>
    

    There might be edge cases where this doesn't work however (inheritance, imported methods, built-ins...), as hidden methods/properties (encased in double underscores) are considered to not be part of the class API and their behaviour might change depending on the implementation/version on Python.