How to mock nested functions?

前端 未结 4 1118
南旧
南旧 2020-12-06 04:10

The mocking library I use is ... mock.

I came across this \"mock nested functions\" problem when I tried to write test case for a function(legacy code).

This

相关标签:
4条回答
  • 2020-12-06 05:02

    The only way I've seen this done is to dynamically create a copy of your outer function, modifying the function's code object constants with the code for your mocked function:

    Does an equivalent of override exist for nested functions?

    0 讨论(0)
  • 2020-12-06 05:08

    for example you need to mock nested function calls (chained functions) from Google DRIVE API

    result = get_drive_service().files().insert(body='body', convert=True).execute()   
    

    so you need to patch through functions: service_mock(), files(), insert(), till last execute() response:

    from mock import patch
    with patch('path.to.import.get_drive_service') as service_mock:
       service_mock.return_value.files.return_value.insert.\
       return_value.execute.return_value = {'key': 'value', 'status': 200}
    

    Main scheme: first.return_value.second.return_value.third.return_value.last.return_value = rsp

    0 讨论(0)
  • 2020-12-06 05:11

    One option is to change your function so that it optionally accepts the function to call e.g. if you have:

    def fn_to_test():
      def inner_fn():
        return 1
      return inner_fn() + 3
    

    Change it to:

    def fn_to_test( inner_fn = null )
      def inner_fn_orig():
        return 1
      if inner_fn==null:
        inner_fn = inner_fn_orig
      return fn() + 3
    

    Then "real" uses will get the right inner function, and in your tests you can provide your own.

    fn_to_test() # calls the real inner function
    def my_inner_fn():
      return 3
    fn_to_test( inner_fn=my_inner_fn ) # calls the new version
    

    You could also do this:

    def fn_to_test():
      def inner_fn_orign():
        return 1
      inner_fn = inner_fn_orig
      try:
        inner_fn = fn_to_test.inner_fn
      excecpt AttributeError:
        pass
      return inner_fn() + 3
    

    This way you just define the override:

    fn_to_test() # calls the real inner function
    def my_inner_fn():
      return 3
    fn_to_test.inner_fn = my_inner_fn
    fn_to_test() # calls the new version
    
    0 讨论(0)
  • 2020-12-06 05:15

    Are you trying to replace a nested function with a mock object? If so, that's fairly straightforward, no matter how complicated the function is. You can use a MagicMock to replace pretty much any python object.

    If you need to simulate a function that returns something, you can just set the MagicMock's return_value parameter. It would look something like this:

    >>> super_nested_mock = mock.MagicMock()
    >>> super_nested_mock.return_value = 42
    >>> super_nested_mock()
    42
    

    However, if you're trying to test another piece of code that calls your super_nested function somewhere inside, and want to mock it out, you'll need to use a patch. In the mock library, it will look something like this:

    with patch('super_nested') as super_nested_mock:
        super_nested_mock.return_value = "A good value to test with"
        assert my_function_that_calls_super_nested(5) == 20
    

    Here, anything in the with block that would normally call super_nested instead will call the super_nested_mock and just return the value that you set to it.

    There is some subtlety to what exactly you need to put in the patch call. Mainly, you want to patch the object as the module you're testing would see it. See "where to patch" for more instruction.

    0 讨论(0)
提交回复
热议问题