assertpy2 2.0.0__py3-none-any.whl
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/__init__.py +25 -0
- assertpy2/assertpy.py +468 -0
- assertpy2/base.py +435 -0
- assertpy2/collection.py +217 -0
- assertpy2/contains.py +386 -0
- assertpy2/date.py +219 -0
- assertpy2/dict.py +255 -0
- assertpy2/dynamic.py +113 -0
- assertpy2/exception.py +128 -0
- assertpy2/extracting.py +233 -0
- assertpy2/file.py +276 -0
- assertpy2/helpers.py +273 -0
- assertpy2/numeric.py +555 -0
- assertpy2/py.typed +0 -0
- assertpy2/snapshot.py +217 -0
- assertpy2/string.py +413 -0
- assertpy2-2.0.0.dist-info/METADATA +227 -0
- assertpy2-2.0.0.dist-info/RECORD +20 -0
- assertpy2-2.0.0.dist-info/WHEEL +4 -0
- assertpy2-2.0.0.dist-info/licenses/LICENSE +28 -0
assertpy2/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .assertpy import (
|
|
2
|
+
WarningLoggingAdapter,
|
|
3
|
+
__version__,
|
|
4
|
+
add_extension,
|
|
5
|
+
assert_that,
|
|
6
|
+
assert_warn,
|
|
7
|
+
fail,
|
|
8
|
+
remove_extension,
|
|
9
|
+
soft_assertions,
|
|
10
|
+
soft_fail,
|
|
11
|
+
)
|
|
12
|
+
from .file import contents_of
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"WarningLoggingAdapter",
|
|
16
|
+
"__version__",
|
|
17
|
+
"add_extension",
|
|
18
|
+
"assert_that",
|
|
19
|
+
"assert_warn",
|
|
20
|
+
"contents_of",
|
|
21
|
+
"fail",
|
|
22
|
+
"remove_extension",
|
|
23
|
+
"soft_assertions",
|
|
24
|
+
"soft_fail",
|
|
25
|
+
]
|
assertpy2/assertpy.py
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
# Copyright (c) 2015-2019, Activision Publishing, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
# are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
# list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
|
16
|
+
# specific prior written permission.
|
|
17
|
+
#
|
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
22
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
25
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
|
|
29
|
+
"""Assertion library for python unit testing with a fluent API"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import contextlib
|
|
34
|
+
import inspect
|
|
35
|
+
import logging
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
import types
|
|
39
|
+
from collections.abc import Iterator
|
|
40
|
+
from typing import TYPE_CHECKING
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from typing_extensions import Self
|
|
44
|
+
|
|
45
|
+
from .base import BaseMixin
|
|
46
|
+
from .collection import CollectionMixin
|
|
47
|
+
from .contains import ContainsMixin
|
|
48
|
+
from .date import DateMixin
|
|
49
|
+
from .dict import DictMixin
|
|
50
|
+
from .dynamic import DynamicMixin
|
|
51
|
+
from .exception import ExceptionMixin
|
|
52
|
+
from .extracting import ExtractingMixin
|
|
53
|
+
from .file import FileMixin
|
|
54
|
+
from .helpers import HelpersMixin
|
|
55
|
+
from .numeric import NumericMixin
|
|
56
|
+
from .snapshot import SnapshotMixin
|
|
57
|
+
from .string import StringMixin
|
|
58
|
+
|
|
59
|
+
__version__ = "2.0.0"
|
|
60
|
+
|
|
61
|
+
__tracebackhide__ = True # clean tracebacks via py.test integration
|
|
62
|
+
contextlib.__tracebackhide__ = True # monkey patch contextlib with clean py.test tracebacks
|
|
63
|
+
|
|
64
|
+
# assertpy files
|
|
65
|
+
ASSERTPY_FILES = [
|
|
66
|
+
os.path.join("assertpy2", file)
|
|
67
|
+
for file in [
|
|
68
|
+
"assertpy.py",
|
|
69
|
+
"base.py",
|
|
70
|
+
"collection.py",
|
|
71
|
+
"contains.py",
|
|
72
|
+
"date.py",
|
|
73
|
+
"dict.py",
|
|
74
|
+
"dynamic.py",
|
|
75
|
+
"exception.py",
|
|
76
|
+
"extracting.py",
|
|
77
|
+
"file.py",
|
|
78
|
+
"helpers.py",
|
|
79
|
+
"numeric.py",
|
|
80
|
+
"snapshot.py",
|
|
81
|
+
"string.py",
|
|
82
|
+
]
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
# soft assertions
|
|
86
|
+
_soft_ctx = 0
|
|
87
|
+
_soft_err = []
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@contextlib.contextmanager
|
|
91
|
+
def soft_assertions() -> Iterator[None]:
|
|
92
|
+
"""Create a soft assertion context.
|
|
93
|
+
|
|
94
|
+
Normally, any assertion failure will halt test execution immediately by raising an error.
|
|
95
|
+
Soft assertions are way to collect assertion failures (and failure messages) together, to be
|
|
96
|
+
raised all at once at the end, without halting your test.
|
|
97
|
+
|
|
98
|
+
Examples:
|
|
99
|
+
Create a soft assertion context, and some failing tests::
|
|
100
|
+
|
|
101
|
+
from assertpy2 import assert_that, soft_assertions
|
|
102
|
+
|
|
103
|
+
with soft_assertions():
|
|
104
|
+
assert_that('foo').is_length(4)
|
|
105
|
+
assert_that('foo').is_empty()
|
|
106
|
+
assert_that('foo').is_false()
|
|
107
|
+
assert_that('foo').is_digit()
|
|
108
|
+
assert_that('123').is_alpha()
|
|
109
|
+
|
|
110
|
+
When the context ends, any assertion failures are collected together and a single
|
|
111
|
+
``AssertionError`` is raised::
|
|
112
|
+
|
|
113
|
+
AssertionError: soft assertion failures:
|
|
114
|
+
1. Expected <foo> to be of length <4>, but was <3>.
|
|
115
|
+
2. Expected <foo> to be empty string, but was not.
|
|
116
|
+
3. Expected <False>, but was not.
|
|
117
|
+
4. Expected <foo> to contain only digits, but did not.
|
|
118
|
+
5. Expected <123> to contain only alphabetic chars, but did not.
|
|
119
|
+
|
|
120
|
+
Note:
|
|
121
|
+
The soft assertion context only collects *assertion* failures, other errors such as
|
|
122
|
+
``TypeError`` or ``ValueError`` are always raised immediately. Triggering an explicit test
|
|
123
|
+
failure with :meth:`fail` will similarly halt execution immediately. If you need more
|
|
124
|
+
forgiving behavior, use :meth:`soft_fail` to add a failure message without halting test
|
|
125
|
+
execution.
|
|
126
|
+
"""
|
|
127
|
+
global _soft_ctx
|
|
128
|
+
global _soft_err
|
|
129
|
+
|
|
130
|
+
# init ctx
|
|
131
|
+
if _soft_ctx == 0:
|
|
132
|
+
_soft_err = []
|
|
133
|
+
_soft_ctx += 1
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
yield
|
|
137
|
+
finally:
|
|
138
|
+
# reset ctx
|
|
139
|
+
_soft_ctx -= 1
|
|
140
|
+
|
|
141
|
+
if _soft_err and _soft_ctx == 0:
|
|
142
|
+
out = "soft assertion failures:\n" + "\n".join("%d. %s" % (i + 1, msg) for i, msg in enumerate(_soft_err))
|
|
143
|
+
_soft_err = []
|
|
144
|
+
raise AssertionError(out)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# factory methods
|
|
148
|
+
def assert_that(val, description=""):
|
|
149
|
+
"""Set the value to be tested, plus an optional description, and allow assertions to be called.
|
|
150
|
+
|
|
151
|
+
This is a factory method for the :class:`AssertionBuilder`, and the single most important
|
|
152
|
+
method in all of assertpy.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
val: the value to be tested (aka the actual value)
|
|
156
|
+
description (str, optional): the extra error message description. Defaults to ``''``
|
|
157
|
+
(aka empty string)
|
|
158
|
+
|
|
159
|
+
Examples:
|
|
160
|
+
Just import it once at the top of your test file, and away you go...::
|
|
161
|
+
|
|
162
|
+
from assertpy2 import assert_that
|
|
163
|
+
|
|
164
|
+
def test_something():
|
|
165
|
+
assert_that(1 + 2).is_equal_to(3)
|
|
166
|
+
assert_that('foobar').is_length(6).starts_with('foo').ends_with('bar')
|
|
167
|
+
assert_that(['a', 'b', 'c']).contains('a').does_not_contain('x')
|
|
168
|
+
"""
|
|
169
|
+
global _soft_ctx
|
|
170
|
+
if _soft_ctx:
|
|
171
|
+
return _builder(val, description, "soft")
|
|
172
|
+
return _builder(val, description)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def assert_warn(val, description="", logger=None):
|
|
176
|
+
"""Set the value to be tested, and optional description and logger, and allow assertions to be
|
|
177
|
+
called, but never fail, only log warnings.
|
|
178
|
+
|
|
179
|
+
This is a factory method for the :class:`AssertionBuilder`, but unlike :meth:`assert_that` an
|
|
180
|
+
`AssertionError` is never raised, and execution is never halted. Instead, any assertion failures
|
|
181
|
+
results in a warning message being logged. Uses the given logger, or defaults to a simple logger
|
|
182
|
+
that prints warnings to ``stdout``.
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
val: the value to be tested (aka the actual value)
|
|
187
|
+
description (str, optional): the extra error message description. Defaults to ``''``
|
|
188
|
+
(aka empty string)
|
|
189
|
+
logger (Logger, optional): the logger for warning message on assertion failure. Defaults to ``None``
|
|
190
|
+
(aka use the default simple logger that prints warnings to ``stdout``)
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
Usage::
|
|
194
|
+
|
|
195
|
+
from assertpy2 import assert_warn
|
|
196
|
+
|
|
197
|
+
assert_warn('foo').is_length(4)
|
|
198
|
+
assert_warn('foo').is_empty()
|
|
199
|
+
assert_warn('foo').is_false()
|
|
200
|
+
assert_warn('foo').is_digit()
|
|
201
|
+
assert_warn('123').is_alpha()
|
|
202
|
+
|
|
203
|
+
Even though all of the above assertions fail, ``AssertionError`` is never raised and
|
|
204
|
+
test execution is never halted. Instead, the failed assertions merely log the following
|
|
205
|
+
warning messages to ``stdout``::
|
|
206
|
+
|
|
207
|
+
2019-10-27 20:00:35 WARNING [test_foo.py:23]: Expected <foo> to be of length <4>, but was <3>.
|
|
208
|
+
2019-10-27 20:00:35 WARNING [test_foo.py:24]: Expected <foo> to be empty string, but was not.
|
|
209
|
+
2019-10-27 20:00:35 WARNING [test_foo.py:25]: Expected <False>, but was not.
|
|
210
|
+
2019-10-27 20:00:35 WARNING [test_foo.py:26]: Expected <foo> to contain only digits, but did not.
|
|
211
|
+
2019-10-27 20:00:35 WARNING [test_foo.py:27]: Expected <123> to contain only alphabetic chars, but did not.
|
|
212
|
+
|
|
213
|
+
Tip:
|
|
214
|
+
Use :meth:`assert_warn` if and only if you have a *really* good reason to log assertion
|
|
215
|
+
failures instead of failing.
|
|
216
|
+
"""
|
|
217
|
+
return _builder(val, description, "warn", logger=logger)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def fail(msg=""):
|
|
221
|
+
"""Force immediate test failure with the given message.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
msg (str, optional): the failure message. Defaults to ``''``
|
|
225
|
+
|
|
226
|
+
Examples:
|
|
227
|
+
Fail a test::
|
|
228
|
+
|
|
229
|
+
from assertpy2 import assert_that, fail
|
|
230
|
+
|
|
231
|
+
def test_fail():
|
|
232
|
+
fail('forced fail!')
|
|
233
|
+
|
|
234
|
+
If you wanted to test for a known failure, here is a useful pattern::
|
|
235
|
+
|
|
236
|
+
import operator
|
|
237
|
+
|
|
238
|
+
def test_adder_bad_arg():
|
|
239
|
+
try:
|
|
240
|
+
operator.add(1, 'bad arg')
|
|
241
|
+
fail('should have raised error')
|
|
242
|
+
except TypeError as e:
|
|
243
|
+
assert_that(str(e)).contains('unsupported operand')
|
|
244
|
+
"""
|
|
245
|
+
raise AssertionError("Fail: %s!" % msg if msg else "Fail!")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def soft_fail(msg=""):
|
|
249
|
+
"""Within a :meth:`soft_assertions` context, append the failure message to the soft error list,
|
|
250
|
+
but do not halt test execution.
|
|
251
|
+
|
|
252
|
+
Otherwise, outside the context, acts identical to :meth:`fail` and forces immediate test
|
|
253
|
+
failure with the given message.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
msg (str, optional): the failure message. Defaults to ``''``
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
Failing soft assertions::
|
|
260
|
+
|
|
261
|
+
from assertpy2 import assert_that, soft_assertions, soft_fail
|
|
262
|
+
|
|
263
|
+
with soft_assertions():
|
|
264
|
+
assert_that(1).is_equal_to(2)
|
|
265
|
+
soft_fail('my message')
|
|
266
|
+
assert_that('foo').is_equal_to('bar')
|
|
267
|
+
|
|
268
|
+
Fails, and outputs the following soft error list::
|
|
269
|
+
|
|
270
|
+
AssertionError: soft assertion failures:
|
|
271
|
+
1. Expected <1> to be equal to <2>, but was not.
|
|
272
|
+
2. Fail: my message!
|
|
273
|
+
3. Expected <foo> to be equal to <bar>, but was not.
|
|
274
|
+
|
|
275
|
+
"""
|
|
276
|
+
global _soft_ctx
|
|
277
|
+
if _soft_ctx:
|
|
278
|
+
global _soft_err
|
|
279
|
+
_soft_err.append("Fail: %s!" % msg if msg else "Fail!")
|
|
280
|
+
return
|
|
281
|
+
fail(msg)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# assertion extensions
|
|
285
|
+
_extensions = {}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def add_extension(func):
|
|
289
|
+
"""Add a new user-defined custom assertion to assertpy.
|
|
290
|
+
|
|
291
|
+
Once the assertion is registered with assertpy, use it like any other assertion. Pass val to
|
|
292
|
+
:meth:`assert_that`, and then call it.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
func: the assertion function (to be added)
|
|
296
|
+
|
|
297
|
+
Examples:
|
|
298
|
+
Usage::
|
|
299
|
+
|
|
300
|
+
from assertpy2 import add_extension
|
|
301
|
+
|
|
302
|
+
def is_5(self):
|
|
303
|
+
if self.val != 5:
|
|
304
|
+
return self.error(f'{self.val} is NOT 5!')
|
|
305
|
+
return self
|
|
306
|
+
|
|
307
|
+
add_extension(is_5)
|
|
308
|
+
|
|
309
|
+
def test_5():
|
|
310
|
+
assert_that(5).is_5()
|
|
311
|
+
|
|
312
|
+
def test_6():
|
|
313
|
+
assert_that(6).is_5() # fails
|
|
314
|
+
# 6 is NOT 5!
|
|
315
|
+
"""
|
|
316
|
+
if not callable(func):
|
|
317
|
+
raise TypeError("func must be callable")
|
|
318
|
+
_extensions[func.__name__] = func
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def remove_extension(func):
|
|
322
|
+
"""Remove a user-defined custom assertion.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
func: the assertion function (to be removed)
|
|
326
|
+
|
|
327
|
+
Examples:
|
|
328
|
+
Usage::
|
|
329
|
+
|
|
330
|
+
from assertpy2 import remove_extension
|
|
331
|
+
|
|
332
|
+
remove_extension(is_5)
|
|
333
|
+
"""
|
|
334
|
+
if not callable(func):
|
|
335
|
+
raise TypeError("func must be callable")
|
|
336
|
+
if func.__name__ in _extensions:
|
|
337
|
+
del _extensions[func.__name__]
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _builder(val, description="", kind=None, expected=None, logger=None):
|
|
341
|
+
"""Internal helper to build a new :class:`AssertionBuilder` instance and glue on any extension methods."""
|
|
342
|
+
ab = AssertionBuilder(val, description, kind, expected, logger)
|
|
343
|
+
if _extensions:
|
|
344
|
+
# glue extension method onto new builder instance
|
|
345
|
+
for name, func in _extensions.items():
|
|
346
|
+
meth = types.MethodType(func, ab)
|
|
347
|
+
setattr(ab, name, meth)
|
|
348
|
+
return ab
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# warnings
|
|
352
|
+
class WarningLoggingAdapter(logging.LoggerAdapter):
|
|
353
|
+
"""Logging adapter to unwind the stack to get the correct callee filename and line number."""
|
|
354
|
+
|
|
355
|
+
def process(self, msg, kwargs):
|
|
356
|
+
def _unwind(frame):
|
|
357
|
+
# walk all the frames
|
|
358
|
+
frames = []
|
|
359
|
+
while frame:
|
|
360
|
+
frames.append((frame.f_code.co_filename, frame.f_lineno))
|
|
361
|
+
frame = frame.f_back
|
|
362
|
+
|
|
363
|
+
# in reverse, find the first assertpy frame (and return the previous one)
|
|
364
|
+
prev = None
|
|
365
|
+
for frame in reversed(frames):
|
|
366
|
+
for f in ASSERTPY_FILES:
|
|
367
|
+
if frame[0].endswith(f):
|
|
368
|
+
return prev
|
|
369
|
+
prev = frame
|
|
370
|
+
|
|
371
|
+
filename, lineno = _unwind(inspect.currentframe())
|
|
372
|
+
return "[%s:%d]: %s" % (os.path.basename(filename), lineno, msg), kwargs
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
_logger = logging.getLogger("assertpy2")
|
|
376
|
+
_handler = logging.StreamHandler(sys.stdout)
|
|
377
|
+
_handler.setLevel(logging.WARNING)
|
|
378
|
+
_format = logging.Formatter("%(asctime)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
|
379
|
+
_handler.setFormatter(_format)
|
|
380
|
+
_logger.addHandler(_handler)
|
|
381
|
+
_default_logger = WarningLoggingAdapter(_logger, None)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class AssertionBuilder(
|
|
385
|
+
StringMixin,
|
|
386
|
+
SnapshotMixin,
|
|
387
|
+
NumericMixin,
|
|
388
|
+
HelpersMixin,
|
|
389
|
+
FileMixin,
|
|
390
|
+
ExtractingMixin,
|
|
391
|
+
ExceptionMixin,
|
|
392
|
+
DynamicMixin,
|
|
393
|
+
DictMixin,
|
|
394
|
+
DateMixin,
|
|
395
|
+
ContainsMixin,
|
|
396
|
+
CollectionMixin,
|
|
397
|
+
BaseMixin,
|
|
398
|
+
):
|
|
399
|
+
"""The main assertion class. Never call the constructor directly, always use the
|
|
400
|
+
:meth:`assert_that` helper instead. Or if you just want warning messages, use the
|
|
401
|
+
:meth:`assert_warn` helper.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
val: the value to be tested (aka the actual value)
|
|
405
|
+
description (str, optional): the extra error message description. Defaults to ``''``
|
|
406
|
+
(aka empty string)
|
|
407
|
+
kind (str, optional): the kind of assertions, one of ``None``, ``soft``, or ``warn``.
|
|
408
|
+
Defaults to ``None``
|
|
409
|
+
expected (Error, optional): the expected exception. Defaults to ``None``
|
|
410
|
+
logger (Logger, optional): the logger for warning messages. Defaults to ``None``
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
def __init__(self, val, description="", kind=None, expected=None, logger=None):
|
|
414
|
+
"""Never call this constructor directly."""
|
|
415
|
+
self.val = val
|
|
416
|
+
self.description = description
|
|
417
|
+
self.kind = kind
|
|
418
|
+
self.expected = expected
|
|
419
|
+
self.logger = logger if logger else _default_logger
|
|
420
|
+
|
|
421
|
+
def builder(self, val, description="", kind=None, expected=None, logger=None):
|
|
422
|
+
"""Helper to build a new :class:`AssertionBuilder` instance. Use this only if not chaining to ``self``.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
val: the value to be tested (aka the actual value)
|
|
426
|
+
description (str, optional): the extra error message description. Defaults to ``''``
|
|
427
|
+
(aka empty string)
|
|
428
|
+
kind (str, optional): the kind of assertions, one of ``None``, ``soft``, or ``warn``.
|
|
429
|
+
Defaults to ``None``
|
|
430
|
+
expected (Error, optional): the expected exception. Defaults to ``None``
|
|
431
|
+
logger (Logger, optional): the logger for warning messages. Defaults to ``None``
|
|
432
|
+
"""
|
|
433
|
+
return _builder(val, description, kind, expected, logger)
|
|
434
|
+
|
|
435
|
+
def error(self, msg) -> Self:
|
|
436
|
+
"""Helper to raise an ``AssertionError`` with the given message.
|
|
437
|
+
|
|
438
|
+
If an error description is set by :meth:`~assertpy.base.BaseMixin.described_as`, then that
|
|
439
|
+
description is prepended to the error message.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
msg: the error message
|
|
443
|
+
|
|
444
|
+
Examples:
|
|
445
|
+
Used to fail an assertion::
|
|
446
|
+
|
|
447
|
+
if self.val != other:
|
|
448
|
+
return self.error('Expected <%s> to be equal to <%s>, but was not.' % (self.val, other))
|
|
449
|
+
|
|
450
|
+
Raises:
|
|
451
|
+
AssertionError: always raised unless ``kind`` is ``warn`` (as set when using an
|
|
452
|
+
:meth:`assert_warn` assertion) or ``kind`` is ``soft`` (as set when inside a
|
|
453
|
+
:meth:`soft_assertions` context).
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
AssertionBuilder: returns this instance to chain to the next assertion, but only when
|
|
457
|
+
``AssertionError`` is not raised, as is the case when ``kind`` is ``warn`` or ``soft``.
|
|
458
|
+
"""
|
|
459
|
+
out = "%s%s" % ("[%s] " % self.description if len(self.description) > 0 else "", msg)
|
|
460
|
+
if self.kind == "warn":
|
|
461
|
+
self.logger.warning(out)
|
|
462
|
+
return self
|
|
463
|
+
elif self.kind == "soft":
|
|
464
|
+
global _soft_err
|
|
465
|
+
_soft_err.append(out)
|
|
466
|
+
return self
|
|
467
|
+
else:
|
|
468
|
+
raise AssertionError(out)
|