The patch decorators are used for patching objects only within the scope of the function they decorate. They automatically handle the unpatching for you, even if exceptions are raise. All of these functions can also be used in with statements.
patch decorates a function. Inside the body of the function, the target (specified in the form ‘PackageName.ModuleName.ClassName’) is patched with a new object. When the function exits the patch is undone.
The target is imported and the specified attribute patched with the new object - so it must be importable from the environment you are calling the decorator from.
If new is omitted, then a new Mock is created and passed in as an extra argument to the decorated function:
@patch('Package.ModuleName.ClassName') def test_something(self, MockClass): "test something"
The spec keyword argument is passed to the Mock if patch is creating one for you.
In addition you can pass spec=True, which causes patch to pass in the object being mocked as the spec object. If you are using patch to mock out a class, then the object you are interested in will probably be the return value of the Mock (the instance it returns when called). Because of this, if you use ‘spec=True’ and are patching a class (and having patch create a Mock for you) then the object being patched will be used as a spec for both the Mock and its return value.
Using the class as a spec object for the created Mock (and return value) means that the Mock will raise an exception if the code attempts to access any attributes that don’t exist.
@patch('Package.ModuleName.ClassName') def test_something(self, MockClass): instance = ClassName() self.assertRaises(AttributeError, lambda: instance.fake_attribute)
Patch can be used with the with statement - if this is available in your version of Python. Here the patching applies to the indented block after the with statement. Note that the patched object can always appear after the “as” - even if an object to be patched was specified, though it can be omitted.
with patch('Package.ModuleName.ClassName') as MockClass: instance = ClassName() self.assertRaises(AttributeError, lambda: instance.fake_attribute)
By default patch will fail to replace attributes that don’t exist. If you pass in ‘create=True’ and the attribute doesn’t exist, patch will create the attribute for you when the patched function is called, and delete it again afterwards. This is useful for writing tests against attributes that your production code creates at runtime. It is off by by default because it can be dangerous. With it switched on you can write passing tests against APIs that don’t actually exist!
You can either call it with three arguments or two arguments. The three argument form takes the object to be patched, the attribute name and the object to replace the attribute with.
When calling with the two argument form you omit the replacement object, and a mock is created for you and passed in as an extra argument to the decorated function:
@patch_object(SomeClass, 'classmethod') def test_something(self, mockMethod): SomeClass.classmethod(3) mockMethod.assert_called_with(3)
spec and create have the same meaning as for the patch decorator.
patch_object is also a context manager and can be used with with statements in the same way as patch.
If you want to perform multiple patches then you can simply stack up the decorators.
You can stack up multiple patch decorators using this pattern:
@patch('module.ClassName1') @patch('module.ClassName2') def testMethod(self, MockClass2, MockClass1): ClassName1() ClassName2() self.assertEquals(MockClass1.called, "ClassName1 not patched") self.assertEquals(MockClass2.called, "ClassName2 not patched")
Like all context-managers patches can be nested using contextlib’s nested function - every patching will appear in the tuple after “as”.
from contextlib import nested with nested(patch('Package.ModuleName.ClassName'), patch('Package.ModuleName.ClassName2', TestUtils.MockClass2)) as (MockClass1, MockClass2): instance = ClassName(ClassName2()) self.assertEquals(instance.f(), "expected")
Since version 0.6.0 both patch and patch_object have been able to correctly patch and restore descriptors; class methods, static methods and properties. You should patch these on the class rather than an instance:
@patch('module.ClassName.static') def testMethod(self, mockStatic): ClassName.static('foo') mockStatic.assert_called_with('foo')