pytest-extended-mock 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pytest-extended-mock
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: a pytest extension for easy mock setup
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Adam Davenport
|
|
7
|
+
Author-email: adam006@gmail.com
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Classifier: Framework :: Pytest
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Topic :: Software Development :: Testing
|
|
12
|
+
Requires-Dist: pytest (>=8.3.5,<9.0.0)
|
|
13
|
+
Requires-Dist: pytest-mock (>=3.14.0,<4.0.0)
|
|
14
|
+
Project-URL: Homepage, https://github.com/adam006/pytest-mock-setup
|
|
15
|
+
Project-URL: Repository, https://github.com/adam006/pytest-mock-setup
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# ExtendedMock Pytest Plugin
|
|
19
|
+
|
|
20
|
+
Welcome to the **ExtendedMock Pytest Plugin**! This plugin enhances Python's `unittest.mock.MagicMock` with additional functionality inspired by the setup and verification patterns found in popular .NET mocking libraries like **Moq**. If you've ever used Moq and loved its fluent API for configuring mock behavior, this plugin will feel like home.
|
|
21
|
+
|
|
22
|
+
## What is ExtendedMock?
|
|
23
|
+
|
|
24
|
+
`ExtendedMock` is a drop-in replacement for `unittest.mock.MagicMock` that adds a `setup` method, allowing you to configure mock behavior for specific arguments. This makes it easier to write expressive and precise tests by defining exactly what your mock should return (or raise) when called with certain arguments.
|
|
25
|
+
|
|
26
|
+
### Key Features:
|
|
27
|
+
- **Fluent API**: Configure mock behavior using a clean, chainable `setup` method.
|
|
28
|
+
- **Argument-Specific Behavior**: Define return values or exceptions for specific argument combinations.
|
|
29
|
+
- **Seamless Integration**: Works out of the box with `pytest` and replaces `MagicMock` automatically.
|
|
30
|
+
- **Familiar to .NET Developers**: If you've used Moq, you'll feel right at home with the `setup` method.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Basic Example
|
|
37
|
+
|
|
38
|
+
Here's how you can use `ExtendedMock` in your tests:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from unittest.mock import MagicMock
|
|
42
|
+
|
|
43
|
+
def test_extended_mock():
|
|
44
|
+
mock = MagicMock()
|
|
45
|
+
|
|
46
|
+
# Configure the mock to return "Hello, World!" when called with ("foo", bar=42)
|
|
47
|
+
mock.setup("foo", bar=42, return_value="Hello, World!")
|
|
48
|
+
|
|
49
|
+
# Call the mock with the configured arguments
|
|
50
|
+
result = mock("foo", bar=42)
|
|
51
|
+
|
|
52
|
+
assert result == "Hello, World!"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Raising Exceptions
|
|
56
|
+
|
|
57
|
+
You can also configure the mock to raise an exception for specific arguments:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
def test_extended_mock_with_exception():
|
|
61
|
+
mock = MagicMock()
|
|
62
|
+
|
|
63
|
+
# Configure the mock to raise a ValueError when called with ("error",)
|
|
64
|
+
mock.setup("error", return_value=ValueError("Something went wrong!"))
|
|
65
|
+
|
|
66
|
+
# Call the mock with the configured arguments
|
|
67
|
+
with pytest.raises(ValueError):
|
|
68
|
+
mock("error")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Default Behavior
|
|
72
|
+
|
|
73
|
+
If the mock is called with arguments that don't match any configured setup, it falls back to the default `MagicMock` behavior:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
def test_extended_mock_default_behavior():
|
|
77
|
+
mock = MagicMock()
|
|
78
|
+
|
|
79
|
+
# No setup configured, so it returns a new MagicMock instance
|
|
80
|
+
result = mock("unconfigured", args=123)
|
|
81
|
+
|
|
82
|
+
assert isinstance(result, MagicMock)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Why Use ExtendedMock?
|
|
88
|
+
|
|
89
|
+
If you've ever found yourself writing repetitive code to configure `MagicMock` instances for different argument combinations, `ExtendedMock` is here to save the day. It provides a more expressive and concise way to define mock behavior, making your tests easier to read and maintain.
|
|
90
|
+
|
|
91
|
+
### For .NET Developers
|
|
92
|
+
|
|
93
|
+
If you're coming from the .NET world and have used libraries like **Moq**, you'll find the `setup` method very familiar. It mirrors the `Setup` and `Returns` methods in Moq, allowing you to define mock behavior in a similar way. For example:
|
|
94
|
+
|
|
95
|
+
```csharp
|
|
96
|
+
// Moq in .NET
|
|
97
|
+
var mock = new Mock<IService>();
|
|
98
|
+
mock.Setup(x => x.DoSomething("foo", 42)).Returns("Hello, World!");
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# ExtendedMock in Python
|
|
103
|
+
mock = MagicMock()
|
|
104
|
+
mock.setup("foo", 42, return_value="Hello, World!")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## How It Works
|
|
110
|
+
|
|
111
|
+
The `ExtendedMock` class extends `unittest.mock.MagicMock` and adds a `_setup_data` dictionary to store configurations. When the mock is called, it checks if the arguments match any configured setup. If a match is found, it returns the configured value or raises the configured exception. If no match is found, it falls back to the default `MagicMock` behavior.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Contributing
|
|
116
|
+
|
|
117
|
+
We welcome contributions! If you have ideas for improvements or find a bug, please open an issue or submit a pull request on [GitHub](https://github.com/yourusername/pytest-extendedmock).
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Keywords
|
|
128
|
+
|
|
129
|
+
- pytest
|
|
130
|
+
- mocking
|
|
131
|
+
- unittest
|
|
132
|
+
- MagicMock
|
|
133
|
+
- Moq
|
|
134
|
+
- .NET
|
|
135
|
+
- testing
|
|
136
|
+
- fluent API
|
|
137
|
+
- mock setup
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
Happy testing! 🚀
|
|
142
|
+
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# ExtendedMock Pytest Plugin
|
|
2
|
+
|
|
3
|
+
Welcome to the **ExtendedMock Pytest Plugin**! This plugin enhances Python's `unittest.mock.MagicMock` with additional functionality inspired by the setup and verification patterns found in popular .NET mocking libraries like **Moq**. If you've ever used Moq and loved its fluent API for configuring mock behavior, this plugin will feel like home.
|
|
4
|
+
|
|
5
|
+
## What is ExtendedMock?
|
|
6
|
+
|
|
7
|
+
`ExtendedMock` is a drop-in replacement for `unittest.mock.MagicMock` that adds a `setup` method, allowing you to configure mock behavior for specific arguments. This makes it easier to write expressive and precise tests by defining exactly what your mock should return (or raise) when called with certain arguments.
|
|
8
|
+
|
|
9
|
+
### Key Features:
|
|
10
|
+
- **Fluent API**: Configure mock behavior using a clean, chainable `setup` method.
|
|
11
|
+
- **Argument-Specific Behavior**: Define return values or exceptions for specific argument combinations.
|
|
12
|
+
- **Seamless Integration**: Works out of the box with `pytest` and replaces `MagicMock` automatically.
|
|
13
|
+
- **Familiar to .NET Developers**: If you've used Moq, you'll feel right at home with the `setup` method.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Basic Example
|
|
20
|
+
|
|
21
|
+
Here's how you can use `ExtendedMock` in your tests:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from unittest.mock import MagicMock
|
|
25
|
+
|
|
26
|
+
def test_extended_mock():
|
|
27
|
+
mock = MagicMock()
|
|
28
|
+
|
|
29
|
+
# Configure the mock to return "Hello, World!" when called with ("foo", bar=42)
|
|
30
|
+
mock.setup("foo", bar=42, return_value="Hello, World!")
|
|
31
|
+
|
|
32
|
+
# Call the mock with the configured arguments
|
|
33
|
+
result = mock("foo", bar=42)
|
|
34
|
+
|
|
35
|
+
assert result == "Hello, World!"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Raising Exceptions
|
|
39
|
+
|
|
40
|
+
You can also configure the mock to raise an exception for specific arguments:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
def test_extended_mock_with_exception():
|
|
44
|
+
mock = MagicMock()
|
|
45
|
+
|
|
46
|
+
# Configure the mock to raise a ValueError when called with ("error",)
|
|
47
|
+
mock.setup("error", return_value=ValueError("Something went wrong!"))
|
|
48
|
+
|
|
49
|
+
# Call the mock with the configured arguments
|
|
50
|
+
with pytest.raises(ValueError):
|
|
51
|
+
mock("error")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Default Behavior
|
|
55
|
+
|
|
56
|
+
If the mock is called with arguments that don't match any configured setup, it falls back to the default `MagicMock` behavior:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
def test_extended_mock_default_behavior():
|
|
60
|
+
mock = MagicMock()
|
|
61
|
+
|
|
62
|
+
# No setup configured, so it returns a new MagicMock instance
|
|
63
|
+
result = mock("unconfigured", args=123)
|
|
64
|
+
|
|
65
|
+
assert isinstance(result, MagicMock)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Why Use ExtendedMock?
|
|
71
|
+
|
|
72
|
+
If you've ever found yourself writing repetitive code to configure `MagicMock` instances for different argument combinations, `ExtendedMock` is here to save the day. It provides a more expressive and concise way to define mock behavior, making your tests easier to read and maintain.
|
|
73
|
+
|
|
74
|
+
### For .NET Developers
|
|
75
|
+
|
|
76
|
+
If you're coming from the .NET world and have used libraries like **Moq**, you'll find the `setup` method very familiar. It mirrors the `Setup` and `Returns` methods in Moq, allowing you to define mock behavior in a similar way. For example:
|
|
77
|
+
|
|
78
|
+
```csharp
|
|
79
|
+
// Moq in .NET
|
|
80
|
+
var mock = new Mock<IService>();
|
|
81
|
+
mock.Setup(x => x.DoSomething("foo", 42)).Returns("Hello, World!");
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# ExtendedMock in Python
|
|
86
|
+
mock = MagicMock()
|
|
87
|
+
mock.setup("foo", 42, return_value="Hello, World!")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## How It Works
|
|
93
|
+
|
|
94
|
+
The `ExtendedMock` class extends `unittest.mock.MagicMock` and adds a `_setup_data` dictionary to store configurations. When the mock is called, it checks if the arguments match any configured setup. If a match is found, it returns the configured value or raises the configured exception. If no match is found, it falls back to the default `MagicMock` behavior.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
We welcome contributions! If you have ideas for improvements or find a bug, please open an issue or submit a pull request on [GitHub](https://github.com/yourusername/pytest-extendedmock).
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Keywords
|
|
111
|
+
|
|
112
|
+
- pytest
|
|
113
|
+
- mocking
|
|
114
|
+
- unittest
|
|
115
|
+
- MagicMock
|
|
116
|
+
- Moq
|
|
117
|
+
- .NET
|
|
118
|
+
- testing
|
|
119
|
+
- fluent API
|
|
120
|
+
- mock setup
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
Happy testing! 🚀
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytest-extended-mock"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "a pytest extension for easy mock setup"
|
|
5
|
+
authors = [{ name = "Adam Davenport", email = "adam006@gmail.com" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
dependencies = ["pytest (>=8.3.5,<9.0.0)", "pytest-mock (>=3.14.0,<4.0.0)"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Framework :: Pytest",
|
|
11
|
+
"Programming Language :: Python",
|
|
12
|
+
"Topic :: Software Development :: Testing",
|
|
13
|
+
]
|
|
14
|
+
[tool.poetry]
|
|
15
|
+
repository = "https://github.com/adam006/pytest-mock-setup"
|
|
16
|
+
homepage = "https://github.com/adam006/pytest-mock-setup"
|
|
17
|
+
license = "MIT"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
21
|
+
build-backend = "poetry.core.masonry.api"
|
|
22
|
+
|
|
23
|
+
[tool.poetry.plugins.pytest11]
|
|
24
|
+
extended_mock = "pytest_extended_mock.plugin"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ExtendedMock(MagicMock):
|
|
5
|
+
def __init__(self, *args, **kwargs):
|
|
6
|
+
super().__init__(*args, **kwargs)
|
|
7
|
+
self._setup_data = {}
|
|
8
|
+
|
|
9
|
+
def setup(self, *args, return_value=None, **kwargs):
|
|
10
|
+
# Default to empty tuple and frozenset
|
|
11
|
+
args = args if args is not None else ()
|
|
12
|
+
kwargs = kwargs if kwargs is not None else {}
|
|
13
|
+
frozen_kwargs = frozenset(kwargs.items()) if kwargs else frozenset()
|
|
14
|
+
|
|
15
|
+
# Store the configuration
|
|
16
|
+
key = (args, frozen_kwargs)
|
|
17
|
+
self._setup_data[key] = return_value
|
|
18
|
+
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def __call__(self, *args, **kwargs):
|
|
22
|
+
if self._setup_data:
|
|
23
|
+
# Check if we have a specific setup for these arguments
|
|
24
|
+
frozen_kwargs = frozenset(kwargs.items()) if kwargs else frozenset()
|
|
25
|
+
exact_key = (args, frozen_kwargs)
|
|
26
|
+
|
|
27
|
+
# If we have a specific setup, return that
|
|
28
|
+
if exact_key in self._setup_data:
|
|
29
|
+
value = self._setup_data[exact_key]
|
|
30
|
+
if isinstance(value, Exception):
|
|
31
|
+
raise value
|
|
32
|
+
return value
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
# Otherwise, fall back to default MagicMock behavior
|
|
36
|
+
return super().__call__(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def pytest_configure(config):
|
|
40
|
+
# Replace MagicMock with our ExtendedMock
|
|
41
|
+
import unittest.mock
|
|
42
|
+
|
|
43
|
+
unittest.mock.MagicMock = ExtendedMock
|