Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions 30 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ Fluent assertions against the value of a given key can be done by prepending `ha

```py
fred = {'first_name': 'Fred', 'last_name': 'Smith', 'shoe_size': 12}

assert_that(fred).has_first_name('Fred')
assert_that(fred).has_last_name('Smith')
assert_that(fred).has_shoe_size(12)
Expand Down Expand Up @@ -534,7 +534,7 @@ As noted above, dynamic assertions also work on dicts:

```py
fred = {'first_name': 'Fred', 'last_name': 'Smith'}

assert_that(fred).has_first_name('Fred')
assert_that(fred).has_last_name('Smith')
```
Expand Down Expand Up @@ -613,24 +613,24 @@ Expected <3> to be equal to <2>, but was not.
The `described_as()` helper causes the custom message `adding stuff` to be prepended to the front of the second error.


#### Soft Assertions
#### Just A Warning

There are times when you don't want to a test to fail at all, instead you only want a warning message. In this case, just replace `assert_that` with `assert_soft`.
There are times when you only want a warning message instead of an failing test. In this case, just replace `assert_that` with `assert_warn`.

```py
assert_soft('foo').is_length(4)
assert_soft('foo').is_empty()
assert_soft('foo').is_false()
assert_soft('foo').is_digit()
assert_soft('123').is_alpha()
assert_soft('foo').is_upper()
assert_soft('FOO').is_lower()
assert_soft('foo').is_equal_to('bar')
assert_soft('foo').is_not_equal_to('foo')
assert_soft('foo').is_equal_to_ignoring_case('BAR')
assert_warn('foo').is_length(4)
assert_warn('foo').is_empty()
assert_warn('foo').is_false()
assert_warn('foo').is_digit()
assert_warn('123').is_alpha()
assert_warn('foo').is_upper()
assert_warn('FOO').is_lower()
assert_warn('foo').is_equal_to('bar')
assert_warn('foo').is_not_equal_to('foo')
assert_warn('foo').is_equal_to_ignoring_case('BAR')
```

The above soft assertions print the following warning messages (but an `AssertionError` is never raised):
The above assertions just print the following warning messages, and an `AssertionError` is never raised:

```
Expected <foo> to be of length <4>, but was <3>.
Expand Down
2 changes: 1 addition & 1 deletion 2 assertpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from __future__ import absolute_import
from .assertpy import assert_that, assert_soft, contents_of, fail, __version__
from .assertpy import assert_that, assert_warn, soft_assertions, contents_of, fail, __version__
53 changes: 44 additions & 9 deletions 53 assertpy/assertpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import numbers
import collections
import inspect
from contextlib import contextmanager

__version__ = '0.9'

Expand All @@ -48,14 +49,43 @@
xrange = xrange
unicode = unicode


### soft assertions ###
_soft_ctx = False
_soft_err = []

@contextmanager
def soft_assertions():
global _soft_ctx
global _soft_err

_soft_ctx = True
_soft_err = []

yield

if _soft_err:
out = 'soft assertion failures:'
for i,msg in enumerate(_soft_err):
out += '\n%d. %s' % (i+1, msg)
raise AssertionError(out)

_soft_err = []
_soft_ctx = False


### factory methods ###
def assert_that(val, description=''):
"""Factory method for the assertion builder with value to be tested and optional description."""
global _soft_ctx
if _soft_ctx:
return AssertionBuilder(val, description, 'soft')
return AssertionBuilder(val, description)

def assert_soft(val, description=''):
def assert_warn(val, description=''):
"""Factory method for the assertion builder with value to be tested, optional description, and
just print assertion failures, don't raise exceptions."""
return AssertionBuilder(val, description, True)
just warn on assertion failures instead of raisings exceptions."""
return AssertionBuilder(val, description, 'warn')

def contents_of(f, encoding='utf-8'):
"""Helper to read the contents of the given file or path into a string with the given encoding.
Expand Down Expand Up @@ -96,14 +126,15 @@ def fail(msg=''):
else:
raise AssertionError('Fail: %s!' % msg)


class AssertionBuilder(object):
"""Assertion builder."""

def __init__(self, val, description, soft=False, expected=None):
def __init__(self, val, description='', kind=None, expected=None):
"""Construct the assertion builder."""
self.val = val
self.description = description
self.soft = soft
self.kind = kind
self.expected = expected

def described_as(self, description):
Expand Down Expand Up @@ -833,7 +864,7 @@ def extracting(self, *names):
else:
raise ValueError('val does not have property or zero-arg method <%s>' % name)
extracted.append(tuple(items) if len(items) > 1 else items[0])
return AssertionBuilder(extracted, self.description)
return AssertionBuilder(extracted, self.description, self.kind)

### dynamic assertions ###
def __getattr__(self, attr):
Expand Down Expand Up @@ -878,7 +909,7 @@ def raises(self, ex):
raise TypeError('val must be function')
if not issubclass(ex, BaseException):
raise TypeError('given arg must be exception')
return AssertionBuilder(self.val, self.description, expected=ex)
return AssertionBuilder(self.val, self.description, self.kind, ex)

def when_called_with(self, *some_args, **some_kwargs):
"""Asserts the val function when invoked with the given args and kwargs raises the expected exception."""
Expand All @@ -889,7 +920,7 @@ def when_called_with(self, *some_args, **some_kwargs):
except BaseException as e:
if issubclass(type(e), self.expected):
# chain on with exception message as val
return AssertionBuilder(str(e), self.description)
return AssertionBuilder(str(e), self.description, self.kind)
else:
# got exception, but wrong type, so raise
self._err('Expected <%s> to raise <%s> when called with (%s), but raised <%s>.' % (
Expand All @@ -908,9 +939,13 @@ def when_called_with(self, *some_args, **some_kwargs):
def _err(self, msg):
"""Helper to raise an AssertionError, and optionally prepend custom description."""
out = '%s%s' % ('[%s] ' % self.description if len(self.description) > 0 else '', msg)
if self.soft:
if self.kind == 'warn':
print(out)
return self
elif self.kind == 'soft':
global _soft_err
_soft_err.append(out)
return self
else:
raise AssertionError(out)

Expand Down
22 changes: 11 additions & 11 deletions 22 tests/test_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import sys
import os
import datetime
from assertpy import assert_that, assert_soft, contents_of, fail
from assertpy import assert_that, assert_warn, contents_of, fail

class TestReadme(object):

Expand Down Expand Up @@ -382,16 +382,16 @@ def test_custom_error_message(self):
assert_that(str(e)).is_equal_to('[adding stuff] Expected <3> to be equal to <2>, but was not.')

def test_soft_assertions(self):
assert_soft('foo').is_length(4)
assert_soft('foo').is_empty()
assert_soft('foo').is_false()
assert_soft('foo').is_digit()
assert_soft('123').is_alpha()
assert_soft('foo').is_upper()
assert_soft('FOO').is_lower()
assert_soft('foo').is_equal_to('bar')
assert_soft('foo').is_not_equal_to('foo')
assert_soft('foo').is_equal_to_ignoring_case('BAR')
assert_warn('foo').is_length(4)
assert_warn('foo').is_empty()
assert_warn('foo').is_false()
assert_warn('foo').is_digit()
assert_warn('123').is_alpha()
assert_warn('foo').is_upper()
assert_warn('FOO').is_lower()
assert_warn('foo').is_equal_to('bar')
assert_warn('foo').is_not_equal_to('foo')
assert_warn('foo').is_equal_to_ignoring_case('BAR')

def test_chaining(self):
fred = Person('Fred','Smith')
Expand Down
107 changes: 67 additions & 40 deletions 107 tests/test_soft.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,48 +28,37 @@

import sys

from assertpy import assert_that, assert_soft, fail
from assertpy import assert_that, soft_assertions, fail

class TestSoft(object):

def test_success(self):
assert_soft('foo').is_length(3)
assert_soft('foo').is_not_empty()
assert_soft('foo').is_true()
assert_soft('foo').is_alpha()
assert_soft('123').is_digit()
assert_soft('foo').is_lower()
assert_soft('FOO').is_upper()
assert_soft('foo').is_equal_to('foo')
assert_soft('foo').is_not_equal_to('bar')
assert_soft('foo').is_equal_to_ignoring_case('FOO')

def test_failures(self):
if sys.version_info[0] == 3:
from io import StringIO
else:
from StringIO import StringIO

# capture stdout
old = sys.stdout
sys.stdout = StringIO()

assert_soft('foo').is_length(4)
assert_soft('foo').is_empty()
assert_soft('foo').is_false()
assert_soft('foo').is_digit()
assert_soft('123').is_alpha()
assert_soft('foo').is_upper()
assert_soft('FOO').is_lower()
assert_soft('foo').is_equal_to('bar')
assert_soft('foo').is_not_equal_to('foo')
assert_soft('foo').is_equal_to_ignoring_case('BAR')

# stop capturing stdout
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = old
def test_success():
with soft_assertions():
assert_that('foo').is_length(3)
assert_that('foo').is_not_empty()
assert_that('foo').is_true()
assert_that('foo').is_alpha()
assert_that('123').is_digit()
assert_that('foo').is_lower()
assert_that('FOO').is_upper()
assert_that('foo').is_equal_to('foo')
assert_that('foo').is_not_equal_to('bar')
assert_that('foo').is_equal_to_ignoring_case('FOO')

def test_failure():
try:
with soft_assertions():
assert_that('foo').is_length(4)
assert_that('foo').is_empty()
assert_that('foo').is_false()
assert_that('foo').is_digit()
assert_that('123').is_alpha()
assert_that('foo').is_upper()
assert_that('FOO').is_lower()
assert_that('foo').is_equal_to('bar')
assert_that('foo').is_not_equal_to('foo')
assert_that('foo').is_equal_to_ignoring_case('BAR')
fail('should have raised error')
except AssertionError as e:
out = str(e)
assert_that(out).contains('Expected <foo> to be of length <4>, but was <3>.')
assert_that(out).contains('Expected <foo> to be empty string, but was not.')
assert_that(out).contains('Expected <False>, but was not.')
Expand All @@ -81,3 +70,41 @@ def test_failures(self):
assert_that(out).contains('Expected <foo> to be not equal to <foo>, but was.')
assert_that(out).contains('Expected <foo> to be case-insensitive equal to <BAR>, but was not.')

def test_failure_chain():
try:
with soft_assertions():
assert_that('foo').is_length(4).is_empty().is_false().is_digit().is_upper()\
.is_equal_to('bar').is_not_equal_to('foo').is_equal_to_ignoring_case('BAR')
fail('should have raised error')
except AssertionError as e:
out = str(e)
assert_that(out).contains('Expected <foo> to be of length <4>, but was <3>.')
assert_that(out).contains('Expected <foo> to be empty string, but was not.')
assert_that(out).contains('Expected <False>, but was not.')
assert_that(out).contains('Expected <foo> to contain only digits, but did not.')
assert_that(out).contains('Expected <foo> to contain only uppercase chars, but did not.')
assert_that(out).contains('Expected <foo> to be equal to <bar>, but was not.')
assert_that(out).contains('Expected <foo> to be not equal to <foo>, but was.')
assert_that(out).contains('Expected <foo> to be case-insensitive equal to <BAR>, but was not.')

def test_expected_exception_success():
with soft_assertions():
assert_that(func_err).raises(RuntimeError).when_called_with('foo').is_equal_to('err')

def test_expected_exception_failure():
try:
with soft_assertions():
assert_that(func_err).raises(RuntimeError).when_called_with('foo').is_equal_to('bar')
assert_that(func_ok).raises(RuntimeError).when_called_with('baz')
fail('should have raised error')
except AssertionError as e:
out = str(e)
assert_that(out).contains('Expected <err> to be equal to <bar>, but was not.')
assert_that(out).contains("Expected <func_ok> to raise <RuntimeError> when called with ('baz').")

def func_ok(arg):
pass

def func_err(arg):
raise RuntimeError('err')

Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.