Package: patch-target
Patch Target: A tiny library for creating valid unittest.mock.patch target argumentsThe ability to monkey patch modules, functions, classes, and class instances is a powerful part of testing Python code; however, the unittest.mock.patch
function depends on the developer providing a precise module path AS A STRING to the
object to be patched.
If you refactor where your modules live or make other changes, you will find the annoying and hard-to-decipher-at-first
errors pointing to an invalid module path string.This small library, consisting of a single exported function,
attempts to make using this function a more straightforward, reliably correct experience.thepatch_targetfunction takes 2 arguments:ahost_moduleof typeModuleTypeand anobject_to_be_patched, which can be of typeAny,
but note thatthe only Patchable types areNamed | str, whereNamedis a protocol describing any object with a__name__attrsee note* on passingstrtypes belowSince you're dealing with python objects instead of strings, you get more guarantees out of the box.
E.g. since you have to pass in a module instead of a string, that means you have to have successfully imported the module
into your test to begin withexample:Given a src code file like this:# myapp.mymodule.mysubmoduleimportdatetimefromuuidimportuuid4my_module_level_attribute="some_value"defget_current_time():returndatetime.datetime.now()defgenerate_new_id():returnuuid4()You can patch the non-deterministic pieces (current time and uuid generation) like so:fromunittest.mockimportpatch,Mockfrommyapp.my_moduleimportmy_submodule# noqaimportdatetimeimportuuidfrompatch_targetimportpatch_target# using the patch decorator to override a module imported into the host_module@patch(patch_target(my_submodule,datetime))# Using string paths the patch arg would be  "myapp.mymodule.my_submodule.datetime"deftest_get_current_time(mock_datetime:Mock)->None:the_beginning_of_time=datetime.datetime(1970,1,1)mock_datetime.datetime.now.return_value=the_beginning_of_timeactual=my_submodule.get_current_time()expected=the_beginning_of_timeassertactual==expected# using the patch context manager override a function imported into the host_moduledeftest_generate_new_id()->None:fake_id="my-super-cool-id"withpatch(patch_target(my_submodule,uuid.uuid4))asmock_uuid4:# Using string paths the patch arg would be  "myapp.mymodule.my_submodule.uuid.uuid4"mock_uuid4.return_value=fake_idactual=my_submodule.generate_new_id()expected=fake_idassertactual==expected# using the patch context manager override a module_attribute defined in the host_moduledeftest_get_module_level_attribute()->None:withpatch(patch_target(my_submodule,"my_module_level_attribute"),"some_other_value"):# Using string paths the patch arg would be  "myapp.mymodule.my_submodule.my_module_level_attribute"actual=my_submodule.my_module_level_attributeexpected="some_other_value"assertactual==expected* Note that though patching module-level attributes using the string name of the variableissupported, you can just use unittest.mock.patch.object the same way. So you don't need this lib to accomplish it.# using unittest.mock.patch.objectfromunittest.mockimportpatchfrommyapp.my_moduleimportmy_submodule# noqadeftest_get_module_level_attribute()->None:withpatch.object(my_submodule,"my_module_level_attribute","some_other_value"):# Using string paths the patch arg would be  "myapp.mymodule.my_submodule.my_module_level_attribute"actual=my_submodule.my_module_level_attributeexpected="some_other_value"assertactual==expected