Create unittest.mock.patch target values from python modules instead of string wrangling
Project description
Patch Target: A tiny library for creating valid unittest.mock.patch target arguments
The 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.
the patch_target
function takes 2 arguments:
- a
host_module
of typeModuleType
- and an
object_to_be_patched
, a Union ofModuleType | Callable[[Any], Any] | Type[Any] | str
(thestr
type is used for overriding module-level attribute)
Since 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 with
example:
Given a src code file like this:
# myapp.mymodule.mysubmodule
import datetime
from uuid import uuid4
my_module_level_attribute = "some_value"
def get_current_time():
return datetime.datetime.now()
def generate_new_id():
return uuid4()
You can patch the non-deterministic pieces (current time and uuid generation) like so:
from unittest.mock import patch, Mock
from myapp.my_module import my_submodule # noqa
import datetime
import uuid
from patch_target import patch_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"
def test_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_time
actual = my_submodule.get_current_time()
expected = the_beginning_of_time
assert actual == expected
# using the patch context manager override a function imported into the host_module
def test_generate_new_id() -> None:
fake_id = "my-super-cool-id"
with patch(patch_target(my_submodule, uuid.uuid4)) as mock_uuid4: # Using string paths the patch arg would be "myapp.mymodule.my_submodule.uuid.uuid4"
mock_uuid4.return_value = fake_id
actual = my_submodule.generate_new_id()
expected = fake_id
assert actual == expected
# using the patch context manager override a module_attribute defined in the host_module
def test_get_module_level_attribute() -> None:
with patch(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_attribute
expected = "some_other_value"
assert actual == expected
Note that though patching module-level attributes using the string name of the variable is supported, 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.object
from unittest.mock import patch
from myapp.my_module import my_submodule # noqa
def test_get_module_level_attribute() -> None:
with patch.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_attribute
expected = "some_other_value"
assert actual == expected
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file patch_target-0.1.4.tar.gz
.
File metadata
- Download URL: patch_target-0.1.4.tar.gz
- Upload date:
- Size: 2.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.6.1 CPython/3.10.9 Darwin/21.6.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5aef7e21e776959ca86c8dda3f05d5f8f2559a573a97a21a95f418ff6276d642 |
|
MD5 | e38726f42af12a3df957825e6b42ed03 |
|
BLAKE2b-256 | 8b9415d39dc1f4a1fc1fe5a79abf0ac88d6659d7688228eea9bf22f7075b3c85 |
File details
Details for the file patch_target-0.1.4-py3-none-any.whl
.
File metadata
- Download URL: patch_target-0.1.4-py3-none-any.whl
- Upload date:
- Size: 2.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.6.1 CPython/3.10.9 Darwin/21.6.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | ef9e3b02930926a99ecddeb347549fab2f60c68ff5ff5e2ee8d5790694817608 |
|
MD5 | 2dc8aaada12ef2678877dd59710d7b0c |
|
BLAKE2b-256 | 6295abded5e7e527a557b4791324a3fe7040af0bb885b69cb1f4ad127a39e757 |