assertpy2 2.3.4__tar.gz → 2.3.5__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.
- {assertpy2-2.3.4 → assertpy2-2.3.5}/PKG-INFO +11 -2
- {assertpy2-2.3.4 → assertpy2-2.3.5}/README.md +10 -1
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/assertpy.py +1 -4
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/collection.py +0 -2
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/exception.py +0 -1
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/helpers.py +0 -6
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/matchers.py +6 -6
- {assertpy2-2.3.4 → assertpy2-2.3.5}/docs/api.md +32 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/pyproject.toml +2 -2
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_matchers.py +67 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/uv.lock +1 -1
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.codecov.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.github/dependabot.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.github/workflows/ci.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.github/workflows/codeql.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.github/workflows/publish.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.github/workflows/scorecard.yml +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/.gitignore +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/CONTRIBUTING.md +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/LICENSE +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/SECURITY.md +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/__init__.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/_mixin_base.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/_typing.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/async_assertions.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/base.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/behave_matchers.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/contains.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/date.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/dict.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/dynamic.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/errors.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/extracting.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/file.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/numeric.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/py.typed +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/pytest_plugin.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/snapshot.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/assertpy2/string.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/docs/logo-dark.svg +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/docs/logo.svg +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_async.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_behave_matchers.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_bool.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_class.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_collection.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_core.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_custom_dict.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_custom_list.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_datetime.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_description.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_dict.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_dict_compare.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_dyn.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_equals.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_errors.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_expected_exception.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_extensions.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_extracting.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_fail.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_file.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_in.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_list.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_matcher_registry.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_matchers_phase3.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_namedtuple.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_none.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_numbers.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_overloads.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_phase2.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_pytest_plugin.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_readme.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_regex_groups.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_same_as.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_snapshots.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_soft.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_soft_fail.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_string.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_structural.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_traceback.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_type.py +0 -0
- {assertpy2-2.3.4 → assertpy2-2.3.5}/tests/test_warn.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assertpy2
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.5
|
|
4
4
|
Summary: Fluent assertion library for Python with composable matchers, structural matching, and full type safety
|
|
5
5
|
Project-URL: Homepage, https://github.com/Solganis/assertpy2
|
|
6
6
|
Project-URL: Repository, https://github.com/Solganis/assertpy2
|
|
@@ -160,7 +160,7 @@ assert_that(items).is_type_of(list).is_length(3).contains("admin")
|
|
|
160
160
|
|
|
161
161
|
## Features
|
|
162
162
|
|
|
163
|
-
- [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`,
|
|
163
|
+
- [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
|
|
164
164
|
- [**Structural matching**](docs/api.md#structural-matching): `matches_structure()` for declarative dict/API response validation.
|
|
165
165
|
- [**Async assertions**](docs/api.md#async-assertions): `eventually()` with polling/retry for async and eventual consistency testing.
|
|
166
166
|
- [**Structured errors**](docs/api.md#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes, pytest plugin with rich diff output.
|
|
@@ -204,6 +204,15 @@ assert_that("hello").satisfies(~match.equal_to("world"))
|
|
|
204
204
|
assert_that(150).satisfies(match.is_negative() | match.greater_than(100))
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
+
Matchers also support `==` directly, so you can use them with plain `assert` or mix into dicts and lists:
|
|
208
|
+
|
|
209
|
+
```py
|
|
210
|
+
from assertpy2 import match
|
|
211
|
+
|
|
212
|
+
assert 42 == match.is_positive()
|
|
213
|
+
assert {"id": 5, "name": "Alice"} == {"id": match.is_positive(), "name": match.is_non_empty_string()}
|
|
214
|
+
```
|
|
215
|
+
|
|
207
216
|
Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `is_zero`, `is_even`, `is_odd`, `is_divisible_by`, `is_callable`, `is_in`, `has_property`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
|
|
208
217
|
|
|
209
218
|
|
|
@@ -125,7 +125,7 @@ assert_that(items).is_type_of(list).is_length(3).contains("admin")
|
|
|
125
125
|
|
|
126
126
|
## Features
|
|
127
127
|
|
|
128
|
-
- [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`,
|
|
128
|
+
- [**Composable matchers**](docs/api.md#composable-matchers): `match.greater_than(5)`, `match.is_uuid()`, combine with `&`, `|`, `~`. Also work with plain `assert ==`.
|
|
129
129
|
- [**Structural matching**](docs/api.md#structural-matching): `matches_structure()` for declarative dict/API response validation.
|
|
130
130
|
- [**Async assertions**](docs/api.md#async-assertions): `eventually()` with polling/retry for async and eventual consistency testing.
|
|
131
131
|
- [**Structured errors**](docs/api.md#structured-errors): `AssertionFailure` with `.actual`, `.expected`, `.diff` attributes, pytest plugin with rich diff output.
|
|
@@ -169,6 +169,15 @@ assert_that("hello").satisfies(~match.equal_to("world"))
|
|
|
169
169
|
assert_that(150).satisfies(match.is_negative() | match.greater_than(100))
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
+
Matchers also support `==` directly, so you can use them with plain `assert` or mix into dicts and lists:
|
|
173
|
+
|
|
174
|
+
```py
|
|
175
|
+
from assertpy2 import match
|
|
176
|
+
|
|
177
|
+
assert 42 == match.is_positive()
|
|
178
|
+
assert {"id": 5, "name": "Alice"} == {"id": match.is_positive(), "name": match.is_non_empty_string()}
|
|
179
|
+
```
|
|
180
|
+
|
|
172
181
|
Available matchers: `equal_to`, `greater_than`, `greater_than_or_equal_to`, `less_than`, `less_than_or_equal_to`, `between`, `close_to`, `is_none`, `is_not_none`, `is_instance_of`, `has_length`, `is_empty`, `is_not_empty`, `is_positive`, `is_negative`, `is_zero`, `is_even`, `is_odd`, `is_divisible_by`, `is_callable`, `is_in`, `has_property`, `contains_string`, `matches_regex`, `is_uuid`, `is_non_empty_string`, `ignore`, `each_item`, `structure`.
|
|
173
182
|
|
|
174
183
|
|
|
@@ -73,7 +73,7 @@ from .numeric import NumericMixin
|
|
|
73
73
|
from .snapshot import SnapshotMixin
|
|
74
74
|
from .string import StringMixin
|
|
75
75
|
|
|
76
|
-
__version__ = "2.3.
|
|
76
|
+
__version__ = "2.3.5"
|
|
77
77
|
|
|
78
78
|
__tracebackhide__ = True # clean tracebacks via py.test integration
|
|
79
79
|
contextlib.__tracebackhide__ = True # ty: ignore[unresolved-attribute] # pytest monkey-patch
|
|
@@ -397,7 +397,6 @@ def _builder(val, description="", kind=None, expected=None, logger=None):
|
|
|
397
397
|
"""Internal helper to build a new :class:`AssertionBuilder` instance and glue on any extension methods."""
|
|
398
398
|
ab = AssertionBuilder(val, description, kind, expected, logger)
|
|
399
399
|
if _extensions:
|
|
400
|
-
# glue extension method onto new builder instance
|
|
401
400
|
for name, func in _extensions.items():
|
|
402
401
|
meth = types.MethodType(func, ab)
|
|
403
402
|
setattr(ab, name, meth)
|
|
@@ -410,13 +409,11 @@ class WarningLoggingAdapter(logging.LoggerAdapter):
|
|
|
410
409
|
|
|
411
410
|
def process(self, msg, kwargs):
|
|
412
411
|
def _unwind(frame):
|
|
413
|
-
# walk all the frames
|
|
414
412
|
frames = []
|
|
415
413
|
while frame:
|
|
416
414
|
frames.append((frame.f_code.co_filename, frame.f_lineno))
|
|
417
415
|
frame = frame.f_back
|
|
418
416
|
|
|
419
|
-
# in reverse, find the first assertpy frame (and return the previous one)
|
|
420
417
|
prev = None
|
|
421
418
|
for frame in reversed(
|
|
422
419
|
frames
|
|
@@ -118,7 +118,6 @@ class CollectionMixin(_MixinBase):
|
|
|
118
118
|
|
|
119
119
|
missing = []
|
|
120
120
|
if hasattr(self.val, "keys") and callable(self.val.keys) and hasattr(self.val, "__getitem__"):
|
|
121
|
-
# flatten superset dicts
|
|
122
121
|
superdict = {}
|
|
123
122
|
for idx, j in enumerate(supersets):
|
|
124
123
|
self._check_dict_like(j, check_values=False, name=f"arg #{idx + 1}")
|
|
@@ -136,7 +135,6 @@ class CollectionMixin(_MixinBase):
|
|
|
136
135
|
f"but {self._fmt_items(missing)} {'was' if len(missing) == 1 else 'were'} missing."
|
|
137
136
|
)
|
|
138
137
|
else:
|
|
139
|
-
# flatten supersets
|
|
140
138
|
superset = set()
|
|
141
139
|
for j in supersets:
|
|
142
140
|
try:
|
|
@@ -73,7 +73,6 @@ class ExceptionMixin(_MixinBase):
|
|
|
73
73
|
if not issubclass(ex, BaseException):
|
|
74
74
|
raise TypeError("given arg must be exception")
|
|
75
75
|
|
|
76
|
-
# chain on with ex as the expected exception
|
|
77
76
|
return self.builder(self.val, self.description, self.kind, ex, self.logger)
|
|
78
77
|
|
|
79
78
|
def when_called_with(self, *some_args, **some_kwargs) -> Self:
|
|
@@ -163,7 +163,6 @@ class HelpersMixin(_MixinBase):
|
|
|
163
163
|
ignores = self._dict_ignore(ignore)
|
|
164
164
|
includes = self._dict_include(include)
|
|
165
165
|
|
|
166
|
-
# guarantee include keys are in val
|
|
167
166
|
if include:
|
|
168
167
|
missing = []
|
|
169
168
|
for i in includes:
|
|
@@ -181,7 +180,6 @@ class HelpersMixin(_MixinBase):
|
|
|
181
180
|
f" but did not include key{missing_suffix} {missing_fmt}."
|
|
182
181
|
)
|
|
183
182
|
|
|
184
|
-
# calc val keys given ignores and includes
|
|
185
183
|
if ignore and include:
|
|
186
184
|
k1 = {k for k in val if k not in ignores and k in includes}
|
|
187
185
|
elif ignore:
|
|
@@ -189,7 +187,6 @@ class HelpersMixin(_MixinBase):
|
|
|
189
187
|
else: # include
|
|
190
188
|
k1 = {k for k in val if k in includes}
|
|
191
189
|
|
|
192
|
-
# calc other keys given ignores and includes
|
|
193
190
|
if ignore and include:
|
|
194
191
|
k2 = {k for k in other if k not in ignores and k in includes}
|
|
195
192
|
elif ignore:
|
|
@@ -198,7 +195,6 @@ class HelpersMixin(_MixinBase):
|
|
|
198
195
|
k2 = {k for k in other if k in includes}
|
|
199
196
|
|
|
200
197
|
if k1 != k2:
|
|
201
|
-
# different set of keys, so not equal
|
|
202
198
|
return True
|
|
203
199
|
else:
|
|
204
200
|
for k in k1:
|
|
@@ -214,10 +210,8 @@ class HelpersMixin(_MixinBase):
|
|
|
214
210
|
else None,
|
|
215
211
|
)
|
|
216
212
|
if subdicts_not_equal:
|
|
217
|
-
# fast fail inside the loop since sub-dicts are not equal
|
|
218
213
|
return True
|
|
219
214
|
elif val[k] != other[k]:
|
|
220
|
-
# fast fail inside the loop since values are not equal
|
|
221
215
|
return True
|
|
222
216
|
return False
|
|
223
217
|
else:
|
|
@@ -43,6 +43,12 @@ class BaseMatcher:
|
|
|
43
43
|
def __invert__(self) -> NotMatcher:
|
|
44
44
|
return NotMatcher(self)
|
|
45
45
|
|
|
46
|
+
def __eq__(self, other: object) -> bool:
|
|
47
|
+
return self.matches(other)
|
|
48
|
+
|
|
49
|
+
def __hash__(self) -> int:
|
|
50
|
+
return id(self)
|
|
51
|
+
|
|
46
52
|
def __repr__(self) -> str:
|
|
47
53
|
return self.describe()
|
|
48
54
|
|
|
@@ -452,8 +458,6 @@ class EndsWithMatcher(BaseMatcher):
|
|
|
452
458
|
|
|
453
459
|
|
|
454
460
|
class IgnoreMatcher(BaseMatcher):
|
|
455
|
-
"""Always matches. Used in structure specs to accept any value for a field."""
|
|
456
|
-
|
|
457
461
|
def matches(self, value: Any) -> bool:
|
|
458
462
|
return True
|
|
459
463
|
|
|
@@ -462,8 +466,6 @@ class IgnoreMatcher(BaseMatcher):
|
|
|
462
466
|
|
|
463
467
|
|
|
464
468
|
class IsUuidMatcher(BaseMatcher):
|
|
465
|
-
"""Matches valid UUID strings."""
|
|
466
|
-
|
|
467
469
|
def matches(self, value: Any) -> bool:
|
|
468
470
|
if not isinstance(value, str):
|
|
469
471
|
return False
|
|
@@ -478,8 +480,6 @@ class IsUuidMatcher(BaseMatcher):
|
|
|
478
480
|
|
|
479
481
|
|
|
480
482
|
class IsNonEmptyStringMatcher(BaseMatcher):
|
|
481
|
-
"""Matches non-empty strings."""
|
|
482
|
-
|
|
483
483
|
def matches(self, value: Any) -> bool:
|
|
484
484
|
return isinstance(value, str) and len(value) > 0
|
|
485
485
|
|
|
@@ -977,6 +977,38 @@ assert_that(50).satisfies(complex_check)
|
|
|
977
977
|
assert_that(-1).satisfies(complex_check)
|
|
978
978
|
```
|
|
979
979
|
|
|
980
|
+
### Using matchers with `==`
|
|
981
|
+
|
|
982
|
+
Matchers implement `__eq__`, so they work with plain `assert` and pytest introspection. No `assert_that()` wrapper needed:
|
|
983
|
+
|
|
984
|
+
```py
|
|
985
|
+
from assertpy2 import match
|
|
986
|
+
|
|
987
|
+
# simple value check
|
|
988
|
+
assert 42 == match.is_positive()
|
|
989
|
+
assert "hello" == match.is_non_empty_string()
|
|
990
|
+
|
|
991
|
+
# dict comparison with matchers as expected values
|
|
992
|
+
assert {"id": 5, "name": "Alice"} == {
|
|
993
|
+
"id": match.is_positive(),
|
|
994
|
+
"name": match.is_non_empty_string(),
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
# list comparison
|
|
998
|
+
assert [1, 2, 3] == [match.is_positive(), match.is_positive(), match.is_positive()]
|
|
999
|
+
|
|
1000
|
+
# composition works too
|
|
1001
|
+
assert 42 == (match.is_positive() & match.less_than(100))
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
On failure, pytest shows the matcher description in the assertion message:
|
|
1005
|
+
|
|
1006
|
+
```
|
|
1007
|
+
AssertionError: assert -5 == a positive value
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
This makes matchers a drop-in addition to existing test suites: add one import, use `match.*` in any `==` comparison, no rewrite required.
|
|
1011
|
+
|
|
980
1012
|
### Available matchers
|
|
981
1013
|
|
|
982
1014
|
| Matcher | Description |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "assertpy2"
|
|
3
|
-
version = "2.3.
|
|
3
|
+
version = "2.3.5"
|
|
4
4
|
description = "Fluent assertion library for Python with composable matchers, structural matching, and full type safety"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "BSD-3-Clause"
|
|
@@ -80,4 +80,4 @@ select = ["E", "E301", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
|
80
80
|
ignore = ["E501"]
|
|
81
81
|
|
|
82
82
|
[tool.ruff.lint.per-file-ignores]
|
|
83
|
-
"tests/*" = ["E741", "SIM102"]
|
|
83
|
+
"tests/*" = ["E741", "SIM102", "SIM300"]
|
|
@@ -562,3 +562,70 @@ class TestDescribeCoverage:
|
|
|
562
562
|
|
|
563
563
|
def test_ends_with_describe(self):
|
|
564
564
|
assert_that(match.ends_with("bar").describe()).is_equal_to("a string ending with <bar>")
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
class TestMatcherEqProtocol:
|
|
568
|
+
def test_eq_positive_match(self):
|
|
569
|
+
assert 5 == match.is_positive()
|
|
570
|
+
|
|
571
|
+
def test_eq_negative_match(self):
|
|
572
|
+
assert (-5 == match.is_positive()) is False
|
|
573
|
+
|
|
574
|
+
def test_eq_equal_to(self):
|
|
575
|
+
assert 42 == match.equal_to(42)
|
|
576
|
+
|
|
577
|
+
def test_eq_between(self):
|
|
578
|
+
assert 5 == match.between(1, 10)
|
|
579
|
+
|
|
580
|
+
def test_eq_string_matcher(self):
|
|
581
|
+
assert "hello" == match.is_non_empty_string()
|
|
582
|
+
|
|
583
|
+
def test_eq_not_equal(self):
|
|
584
|
+
assert -5 != match.is_positive()
|
|
585
|
+
|
|
586
|
+
def test_eq_reverse_order(self):
|
|
587
|
+
assert match.is_positive() == 5
|
|
588
|
+
|
|
589
|
+
def test_eq_dict(self):
|
|
590
|
+
assert {"id": 5, "name": "Alice"} == {"id": match.is_positive(), "name": match.is_non_empty_string()}
|
|
591
|
+
|
|
592
|
+
def test_eq_dict_mismatch(self):
|
|
593
|
+
assert {"id": -5} != {"id": match.is_positive()}
|
|
594
|
+
|
|
595
|
+
def test_eq_nested_dict(self):
|
|
596
|
+
data = {"user": {"name": "Alice", "age": 30}}
|
|
597
|
+
assert data == {"user": {"name": match.is_non_empty_string(), "age": match.is_positive()}}
|
|
598
|
+
|
|
599
|
+
def test_eq_list(self):
|
|
600
|
+
assert [1, 2, 3] == [match.is_positive(), match.is_positive(), match.is_positive()]
|
|
601
|
+
|
|
602
|
+
def test_eq_composition_and(self):
|
|
603
|
+
assert 5 == (match.is_positive() & match.less_than(10))
|
|
604
|
+
|
|
605
|
+
def test_eq_composition_or(self):
|
|
606
|
+
assert -1 == (match.is_positive() | match.is_negative())
|
|
607
|
+
|
|
608
|
+
def test_eq_negated_matcher(self):
|
|
609
|
+
assert -5 == ~match.is_positive()
|
|
610
|
+
|
|
611
|
+
def test_hash_unique_instances(self):
|
|
612
|
+
m1 = match.is_positive()
|
|
613
|
+
m2 = match.is_positive()
|
|
614
|
+
s = {m1, m2}
|
|
615
|
+
assert_that(s).is_length(2)
|
|
616
|
+
|
|
617
|
+
def test_hash_same_instance(self):
|
|
618
|
+
m = match.is_positive()
|
|
619
|
+
s = {m, m}
|
|
620
|
+
assert_that(s).is_length(1)
|
|
621
|
+
|
|
622
|
+
def test_repr_unchanged(self):
|
|
623
|
+
assert_that(repr(match.is_positive())).is_equal_to("a positive value")
|
|
624
|
+
|
|
625
|
+
def test_eq_with_pytest_assert_message(self):
|
|
626
|
+
try:
|
|
627
|
+
assert -5 == match.is_positive()
|
|
628
|
+
except AssertionError:
|
|
629
|
+
pass
|
|
630
|
+
else:
|
|
631
|
+
raise AssertionError("Expected AssertionError")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|