pythonunit-testingpatchcode-injection

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")



Solution

  • Before to show you my code I add some comments:

    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