gerfred
gerfred

Reputation: 23

python unittest How to patch a function with an injected argument and modify it

Try to unittest the function to test which used a function with an injected Element I know how to patch the return value of function_to_patch but how to alter the injected element ?

@dataclass
class Element:
   type: str
   name:str
   tags: List[str]
   nb_elt:int


def function_to_patch(injected_arg:Element )-> Tuple[List[str], int]:
    do_something()
    
    injected_arg.tags=["tag1","tag3"]
    return (["somedata"], 0)


def function_to_test()-> str:
    injected_arg= Element()
    do_something...
    mylist, myval = function_to_patch(injected_arg)
    ....
    return Element.tags[1] 


With this approach the result is not correct The result does not contain the expected "tag3" as I have no idea on how to alter the injected_arg

@mock.patch("function_to_patch")
def TestsFunctionToTest(self,  patch_of_function_to_patch):

  patch_of_function_to_patch.return_value=("my return",3)
 
  result = function_to_test()
  assertEqual(result, "tag3")


Upvotes: 1

Views: 45

Answers (1)

User051209
User051209

Reputation: 2558

Before to show you my code I add some comments:

  • I'm using 3.6.9 so I can't use the decorator @dataclass so I have removed it from your code.
  • I have defined a new function real_patch() that it is use to execute the patch of the function function_to_patch(); the body of this function is the same of the body of the function function_to_patch() with the only difference that it returns (["my return"], 3) and not (["somedata"], 0)
  • I have modified function_to_test() adding to it the argument injected_arg

For other changes see the comments in the code

import unittest
from unittest import mock
from typing import List, Tuple

# Commented the decorator
#@dataclass
class Element:
    type: str = None        # <--- add the init of the attribute
    name: str = None        # <--- add the init of the attribute
    tags: List[str] = []    # <--- add the init of the attribute
    nb_elt: int = None      # <--- add the init of the attribute

# the following is you function without changes
def function_to_patch(injected_arg: Element) -> Tuple[List[str], int]:
    #do_something()
    injected_arg.tags = ["tag1", "tag3"]
    return (["somedata"], 0)

def function_to_test(injected_arg : Element) -> str:
    #injected_arg = Element()   # <---- COMMENT this
    #do_something...
    mylist, myval = function_to_patch(injected_arg)
    print(f"mylist = {mylist}, myval = {myval}")      # <---- ADDED this print
    #....
    #return Element.tags[1]                          # <---- changed the return value
    return injected_arg.tags[1]

# my NEW FUNCTION with the return value (["my return"], 3)
def real_patch(injected_arg: Element) -> Tuple[List[str], int]:
    #do_something()
    injected_arg.tags = ["tag1", "tag3"]
    return (["my return"], 3)


class MyTestCase(unittest.TestCase):
    @mock.patch(__name__ + "." + "function_to_patch")
    def test_injection(self, patch_of_function_to_patch):
        injected_arg = Element()
        patch_of_function_to_patch.side_effect = [real_patch(injected_arg)] # <--- definition of side_effect and not of return_value
        result = function_to_test(injected_arg)
        self.assertEqual(result, "tag3")

if __name__ == '__main__':
    unittest.main()

The test_injection() method is executed with success on my system:

mylist = ['my return'], myval = 3
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Upvotes: 0

Related Questions