Search code examples
pythonunit-testingmockingpatch

patch method that uses external library method calls


The foo class links the external library to an atribute so I can use self.external_class.externalClassMethod. But to do testing, I need to patch out this method call so I can continue testing the rest of the method. I have tried everything in the @patch decorator, nothing worked :(

import os
from unittest import TestCase
from unittest.mock import patch

class Foo(object):
    def __init__(self):
        self.os_handle = os
        self.string = None
    def path(self):
        try:
            print(self.os_handle.getNothing())
        except Exception:
            raise Exception
        self.string = "circumvented the faulty os_handle method call"
    
class TestFoo(TestCase):
    def testClass(self):
        self.assertEqual(1,1)
    def testLibraryCall(self):
        with self.assertRaises(Exception) as cm:
            foo = Foo()
            foo.path()
        self.assertEqual(cm.exception.__class__, Exception)
    # @patch('os', 'getNothing', ...)  # WHAT DO I PATCH?????
    def testLibraryCallNoException(self):
        foo = Foo()
        foo.path()
        self.assertEqual(foo.string, "circumvented the faulty os_handle method call")

save above code in my_class.py and run above code with $ python -m unittest my_class


Solution

  • Try with the following modification:

    import os
    from unittest import TestCase
    from unittest.mock import patch
    
    class Foo(object):
        def __init__(self):
            self.os_handle = os
            self.string = None
        def path(self):
            try:
                print(self.os_handle.getNothing())
            except Exception:
                raise Exception
            self.string = "circumvented the faulty os_handle method call"
        
    class TestFoo(TestCase):
        def testClass(self):
            self.assertEqual(1,1)
        def testLibraryCall(self):
            with self.assertRaises(Exception) as cm:
                foo = Foo()
                foo.path()
            self.assertEqual(cm.exception.__class__, Exception)
        
        def testLibraryCallNoException(self):
            foo = Foo()
            with patch.object(foo, "os_handle") as mock_os_handle: 
                foo.path()
                self.assertEqual(foo.string, "circumvented the faulty os_handle method call")
    

    I have modified only the method testLibraryCallNoException():

    • I have used patch.object() instead patch(): in this way the attribute self.os_handle is substitute by the mock object mock_os_handle. The effect of this substitution is that in your production code the instruction self.os_handle.getNothing() does nothing as you want (I have omitted some details in the explanation!)
    • I have added self. to assertEqual(foo.string, "circumvented the faulty os_handle method call") to solve an other error in the test code.