assertpy
Advanced tools
| from __future__ import absolute_import | ||
| from .assertpy import assert_that, contents_of, fail, __version__ | ||
| from .assertpy import assert_that, assert_soft, contents_of, fail, __version__ |
+213
-71
@@ -1,2 +0,2 @@ | ||
| # Copyright (c) 2015, Activision Publishing, Inc. | ||
| # Copyright (c) 2015-2016, Activision Publishing, Inc. | ||
| # All rights reserved. | ||
@@ -31,3 +31,3 @@ # | ||
| from __future__ import division | ||
| from __future__ import division, print_function | ||
| import re | ||
@@ -38,4 +38,6 @@ import os | ||
| import numbers | ||
| import collections | ||
| import inspect | ||
| __version__ = '0.8' | ||
| __version__ = '0.9' | ||
@@ -51,7 +53,12 @@ if sys.version_info[0] == 3: | ||
| def assert_that(val, description = ''): | ||
| def assert_that(val, description=''): | ||
| """Factory method for the assertion builder with value to be tested and optional description.""" | ||
| return AssertionBuilder(val, description) | ||
| def contents_of(f, encoding = 'utf-8'): | ||
| def assert_soft(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) | ||
| 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. | ||
@@ -88,3 +95,3 @@ Encoding defaults to 'utf-8', other useful encodings are 'ascii' and 'latin-1'.""" | ||
| def fail(msg = ''): | ||
| def fail(msg=''): | ||
| """Force test failure with the given message.""" | ||
@@ -99,6 +106,8 @@ if len(msg) == 0: | ||
| def __init__(self, val, description): | ||
| def __init__(self, val, description, soft=False, expected=None): | ||
| """Construct the assertion builder.""" | ||
| self.val = val | ||
| self.description = description | ||
| self.soft = soft | ||
| self.expected = expected | ||
@@ -122,2 +131,14 @@ def described_as(self, description): | ||
| def is_same_as(self, other): | ||
| """Asserts that the val is the same as the 'other' object being compared to.""" | ||
| if self.val is not other: | ||
| self._err('Expected <%s> to be identical to <%s>, but was not.' % (self.val, other)) | ||
| return self | ||
| def is_not_same_as(self, other): | ||
| """Asserts that the val is the same as the 'other' object being compared to.""" | ||
| if self.val is other: | ||
| self._err('Expected <%s> to be not identical to <%s>, but was not.' % (self.val, other)) | ||
| return self | ||
| def is_true(self): | ||
@@ -290,13 +311,22 @@ """Asserts that val is true.""" | ||
| ### numeric assertions ### | ||
| def _validate_numeric_or_datetime(self, other): | ||
| if type(self.val) is complex or type(other) is complex: | ||
| raise TypeError('ordering is not defined for complex numbers') | ||
| if isinstance(self.val, numbers.Number) is False and type(self.val) is not datetime.datetime: | ||
| raise TypeError('val is not numeric or datetime') | ||
| if type(self.val) is datetime.datetime: | ||
| if type(other) is not datetime.datetime: | ||
| raise TypeError('given arg must be datetime, but was <%s>' % type(other).__name__) | ||
| elif isinstance(other, numbers.Number) is False: | ||
| raise TypeError('given arg must be numeric') | ||
| COMPAREABLE_TYPES = set([datetime.datetime, datetime.timedelta, datetime.date, datetime.time]) | ||
| NON_COMPAREABLE_TYPES = set([complex]) | ||
| def _validate_compareable(self, other): | ||
| self_type = type(self.val) | ||
| other_type = type(other) | ||
| if self_type in self.NON_COMPAREABLE_TYPES: | ||
| raise TypeError('ordering is not defined for type <%s>' % self_type.__name__) | ||
| if self_type in self.COMPAREABLE_TYPES: | ||
| if other_type is not self_type: | ||
| raise TypeError('given arg must be <%s>, but was <%s>' % (self_type.__name__, other_type.__name__)) | ||
| return | ||
| if isinstance(self.val, numbers.Number): | ||
| if not isinstance(other, numbers.Number): | ||
| raise TypeError('given arg must be a number, but was <%s>' % other_type.__name__) | ||
| return | ||
| raise TypeError('ordering is not defined for type <%s>' % self_type.__name__) | ||
| def is_zero(self): | ||
@@ -316,3 +346,3 @@ """Asserts that val is numeric and equal to zero.""" | ||
| """Asserts that val is numeric and is greater than other.""" | ||
| self._validate_numeric_or_datetime(other) | ||
| self._validate_compareable(other) | ||
| if self.val <= other: | ||
@@ -327,3 +357,3 @@ if type(self.val) is datetime.datetime: | ||
| """Asserts that val is numeric and is greater than or equal to other.""" | ||
| self._validate_numeric_or_datetime(other) | ||
| self._validate_compareable(other) | ||
| if self.val < other: | ||
@@ -338,3 +368,3 @@ if type(self.val) is datetime.datetime: | ||
| """Asserts that val is numeric and is less than other.""" | ||
| self._validate_numeric_or_datetime(other) | ||
| self._validate_compareable(other) | ||
| if self.val >= other: | ||
@@ -349,3 +379,3 @@ if type(self.val) is datetime.datetime: | ||
| """Asserts that val is numeric and is less than or equal to other.""" | ||
| self._validate_numeric_or_datetime(other) | ||
| self._validate_compareable(other) | ||
| if self.val > other: | ||
@@ -368,20 +398,25 @@ if type(self.val) is datetime.datetime: | ||
| """Asserts that val is numeric and is between low and high.""" | ||
| if type(self.val) is complex or type(low) is complex or type(high) is complex: | ||
| raise TypeError('ordering is not defined for complex numbers') | ||
| if isinstance(self.val, numbers.Number) is False and type(self.val) is not datetime.datetime: | ||
| raise TypeError('val is not numeric or datetime') | ||
| if type(self.val) is datetime.datetime: | ||
| if type(low) is not datetime.datetime: | ||
| raise TypeError('given low arg must be datetime, but was <%s>' % type(low).__name__) | ||
| if type(high) is not datetime.datetime: | ||
| raise TypeError('given high arg must be datetime, but was <%s>' % type(high).__name__) | ||
| else: | ||
| self_type = type(self.val) | ||
| low_type = type(low) | ||
| high_type = type(high) | ||
| if self_type in self.NON_COMPAREABLE_TYPES: | ||
| raise TypeError('ordering is not defined for type <%s>' % self_type.__name__) | ||
| if self_type in self.COMPAREABLE_TYPES: | ||
| if low_type is not self_type: | ||
| raise TypeError('given low arg must be <%s>, but was <%s>' % (self_type.__name__, low_type.__name__)) | ||
| if high_type is not self_type: | ||
| raise TypeError('given high arg must be <%s>, but was <%s>' % (self_type.__name__, low_type.__name__)) | ||
| elif isinstance(self.val, numbers.Number): | ||
| if isinstance(low, numbers.Number) is False: | ||
| raise TypeError('given low arg must be numeric') | ||
| raise TypeError('given low arg must be numeric, but was <%s>' % low_type.__name__) | ||
| if isinstance(high, numbers.Number) is False: | ||
| raise TypeError('given high arg must be numeric') | ||
| raise TypeError('given high arg must be numeric, but was <%s>' % high_type.__name__) | ||
| else: | ||
| raise TypeError('ordering is not defined for type <%s>' % self_type.__name__) | ||
| if low > high: | ||
| raise ValueError('given low arg must be less than given high arg') | ||
| if self.val < low or self.val > high: | ||
| if type(self.val) is datetime.datetime: | ||
| if self_type is datetime.datetime: | ||
| self._err('Expected <%s> to be between <%s> and <%s>, but was not.' % (self.val.strftime('%Y-%m-%d %H:%M:%S'), low.strftime('%Y-%m-%d %H:%M:%S'), high.strftime('%Y-%m-%d %H:%M:%S'))) | ||
@@ -451,23 +486,40 @@ else: | ||
| def starts_with(self, prefix): | ||
| """Asserts that val is string and starts with prefix.""" | ||
| if not isinstance(self.val, str_types): | ||
| raise TypeError('val is not a string') | ||
| if not isinstance(prefix, str_types): | ||
| raise TypeError('given prefix arg must be a string') | ||
| if len(prefix) == 0: | ||
| raise ValueError('given prefix arg must not be empty') | ||
| if not self.val.startswith(prefix): | ||
| self._err('Expected <%s> to start with <%s>, but did not.' % (self.val, prefix)) | ||
| """Asserts that val is string or iterable and starts with prefix.""" | ||
| if isinstance(self.val, str_types): | ||
| if not isinstance(prefix, str_types): | ||
| raise TypeError('given prefix arg must be a string') | ||
| if len(prefix) == 0: | ||
| raise ValueError('given prefix arg must not be empty') | ||
| if not self.val.startswith(prefix): | ||
| self._err('Expected <%s> to start with <%s>, but did not.' % (self.val, prefix)) | ||
| elif isinstance(self.val, collections.Iterable): | ||
| if len(self.val) == 0: | ||
| raise ValueError('val must not be empty') | ||
| for i in self.val: | ||
| if i != prefix: | ||
| self._err('Expected %s to start with <%s>, but did not.' % (self.val, prefix)) | ||
| break | ||
| else: | ||
| raise TypeError('val is not a string or iterable') | ||
| return self | ||
| def ends_with(self, suffix): | ||
| """Asserts that val is string and ends with suffix.""" | ||
| if not isinstance(self.val, str_types): | ||
| raise TypeError('val is not a string') | ||
| if not isinstance(suffix, str_types): | ||
| raise TypeError('given suffix arg must be a string') | ||
| if len(suffix) == 0: | ||
| raise ValueError('given suffix arg must not be empty') | ||
| if not self.val.endswith(suffix): | ||
| self._err('Expected <%s> to end with <%s>, but did not.' % (self.val, suffix)) | ||
| """Asserts that val is string or iterable and ends with suffix.""" | ||
| if isinstance(self.val, str_types): | ||
| if not isinstance(suffix, str_types): | ||
| raise TypeError('given suffix arg must be a string') | ||
| if len(suffix) == 0: | ||
| raise ValueError('given suffix arg must not be empty') | ||
| if not self.val.endswith(suffix): | ||
| self._err('Expected <%s> to end with <%s>, but did not.' % (self.val, suffix)) | ||
| elif isinstance(self.val, collections.Iterable): | ||
| if len(self.val) == 0: | ||
| raise ValueError('val must not be empty') | ||
| last = None | ||
| for last in self.val: | ||
| pass | ||
| if last != suffix: | ||
| self._err('Expected %s to end with <%s>, but did not.' % (self.val, suffix)) | ||
| else: | ||
| raise TypeError('val is not a string or iterable') | ||
| return self | ||
@@ -545,7 +597,22 @@ | ||
| ### collection assertions ### | ||
| def is_iterable(self): | ||
| """Asserts that val is iterable collection.""" | ||
| if not isinstance(self.val, collections.Iterable): | ||
| self._err('Expected iterable, but was not.') | ||
| return self | ||
| def is_not_iterable(self): | ||
| """Asserts that val is not iterable collection.""" | ||
| if isinstance(self.val, collections.Iterable): | ||
| self._err('Expected not iterable, but was.') | ||
| return self | ||
| ### dict assertions ### | ||
| def contains_key(self, *keys): | ||
| """Asserts the val is a dict and contains the given key or keys. Alias for contains().""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')): | ||
| raise TypeError('val is not dict-like') | ||
| return self.contains(*keys) | ||
@@ -555,4 +622,6 @@ | ||
| """Asserts the val is a dict and does not contain the given key or keys. Alias for does_not_contain().""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')): | ||
| raise TypeError('val is not dict-like') | ||
| return self.does_not_contain(*keys) | ||
@@ -562,4 +631,8 @@ | ||
| """Asserts that val is a dict and contains the given value or values.""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')) or \ | ||
| not hasattr(self.val, 'values') or \ | ||
| not callable(getattr(self.val, 'values')): | ||
| raise TypeError('val is not dict-like') | ||
| if len(values) == 0: | ||
@@ -574,4 +647,8 @@ raise ValueError('one or more value args must be given') | ||
| """Asserts that val is a dict and does not contain the given value or values.""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')) or \ | ||
| not hasattr(self.val, 'values') or \ | ||
| not callable(getattr(self.val, 'values')): | ||
| raise TypeError('val is not dict-like') | ||
| if len(values) == 0: | ||
@@ -590,4 +667,7 @@ raise ValueError('one or more value args must be given') | ||
| """Asserts that val is a dict and contains the given entry or entries.""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')) or \ | ||
| not hasattr(self.val, '__getitem__'): | ||
| raise TypeError('val is not dict-like') | ||
| if len(entries) == 0: | ||
@@ -609,4 +689,7 @@ raise ValueError('one or more entry args must be given') | ||
| """Asserts that val is a dict and does not contain the given entry or entries.""" | ||
| if type(self.val) is not dict: | ||
| raise TypeError('val is not a dict') | ||
| if not isinstance(self.val, collections.Iterable) or \ | ||
| not hasattr(self.val, 'keys') or \ | ||
| not callable(getattr(self.val, 'keys')) or \ | ||
| not hasattr(self.val, '__getitem__'): | ||
| raise TypeError('val is not dict-like') | ||
| if len(entries) == 0: | ||
@@ -717,6 +800,8 @@ raise ValueError('one or more entry args must be given') | ||
| ### collection of objects assertions ### | ||
| def extract(self, *names): | ||
| def extracting(self, *names): | ||
| """Asserts that val is collection, then extracts the named properties or named zero-arg methods into a list (or list of tuples if multiple names are given).""" | ||
| if type(self.val) not in [list, tuple, set]: | ||
| raise TypeError('val is not a collection') | ||
| if not isinstance(self.val, collections.Iterable): | ||
| raise TypeError('val is not iterable') | ||
| if isinstance(self.val, str_types): | ||
| raise TypeError('val must not be string') | ||
| if len(names) == 0: | ||
@@ -728,3 +813,8 @@ raise ValueError('one or more name args must be given') | ||
| for name in names: | ||
| if hasattr(i, name): | ||
| if type(i) is dict: | ||
| if name in i: | ||
| items.append(i[name]) | ||
| else: | ||
| raise ValueError('item keys %s did not contain key <%s>' % (list(i.keys()), name)) | ||
| elif hasattr(i, name): | ||
| attr = getattr(i, name) | ||
@@ -772,9 +862,61 @@ if callable(attr): | ||
| ### expected exceptions ### | ||
| def raises(self, ex): | ||
| """Asserts that val is a function that when invoked raises the given error.""" | ||
| if not inspect.isfunction(self.val): | ||
| 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) | ||
| 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.""" | ||
| if not self.expected: | ||
| raise TypeError('expected exception not set, raises() must be called first') | ||
| try: | ||
| self.val(*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) | ||
| else: | ||
| # got exception, but wrong type, so raise | ||
| self._err('Expected <%s> to raise <%s> when called with (%s), but raised <%s>.' % ( | ||
| self.val.__name__, | ||
| self.expected.__name__, | ||
| self._fmt_args_kwargs(*some_args, **some_kwargs), | ||
| type(e).__name__)) | ||
| # didn't fail as expected, so raise | ||
| self._err('Expected <%s> to raise <%s> when called with (%s).' % ( | ||
| self.val.__name__, | ||
| self.expected.__name__, | ||
| self._fmt_args_kwargs(*some_args, **some_kwargs))) | ||
| ### helpers ### | ||
| def _err(self, msg): | ||
| """Helper to raise an AssertionError, and optionally prepend custom description.""" | ||
| if len(self.description) > 0: | ||
| raise AssertionError('[%s] %s' % (self.description, msg)) | ||
| out = '%s%s' % ('[%s] ' % self.description if len(self.description) > 0 else '', msg) | ||
| if self.soft: | ||
| print(out) | ||
| return self | ||
| else: | ||
| raise AssertionError('%s' % msg) | ||
| raise AssertionError(out) | ||
| def _fmt_args_kwargs(self, *some_args, **some_kwargs): | ||
| """Helper to convert the given args and kwargs into a string.""" | ||
| if some_args: | ||
| out_args = str(some_args).lstrip('(').rstrip(',)') | ||
| if some_kwargs: | ||
| out_kwargs = ', '.join([str(i).lstrip('(').rstrip(')').replace(', ',': ') for i in [ | ||
| (k,some_kwargs[k]) for k in sorted(some_kwargs.keys())]]) | ||
| if some_args and some_kwargs: | ||
| return out_args + ', ' + out_kwargs | ||
| elif some_args: | ||
| return out_args | ||
| elif some_kwargs: | ||
| return out_kwargs | ||
| else: | ||
| return '' | ||
+11
-12
| Metadata-Version: 1.1 | ||
| Name: assertpy | ||
| Version: 0.8 | ||
| Version: 0.9 | ||
| Summary: Assertion framework for python unit testing with a fluent API | ||
@@ -9,3 +9,3 @@ Home-page: https://github.com/ActivisionGameScience/assertpy | ||
| License: BSD | ||
| Download-URL: https://codeload.github.com/ActivisionGameScience/assertpy/tar.gz/0.8 | ||
| Download-URL: https://codeload.github.com/ActivisionGameScience/assertpy/tar.gz/0.9 | ||
| Description: assertpy | ||
@@ -20,16 +20,15 @@ ======== | ||
| Just import the ``assert_that`` function, and away you go: | ||
| Just import the ``assert_that`` function, and away you go:: | ||
| .. code-block:: python | ||
| from assertpy import assert_that | ||
| class TestSomething(object): | ||
| def test_something(self): | ||
| assert_that(1 + 2).is_equal_to(3) | ||
| assert_that('foobar').is_length(6).starts_with('foo').ends_with('bar') | ||
| def test_something(): | ||
| assert_that(1 + 2).is_equal_to(3) | ||
| assert_that('foobar').is_length(6).starts_with('foo').ends_with('bar') | ||
| assert_that(['a', 'b', 'c']).contains('a').does_not_contain('x') | ||
| Of course, ``assertpy`` works best with a python test runner | ||
| like `Nose <http://nose.readthedocs.org/>`_ | ||
| or `pytest <http://pytest.org/latest/contents.html>`_. | ||
| Keywords: test,testing,assert,assertion,assert_that,nose,nosetests,pytest | ||
| like `pytest <http://pytest.org/latest/contents.html>`_ (our favorite) | ||
| or `Nose <http://nose.readthedocs.org/>`_. | ||
| Keywords: test,testing,assert,assertion,asserthat,assert_that,nose,nosetests,pytest | ||
| Platform: UNKNOWN | ||
@@ -46,5 +45,5 @@ Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3.3 | ||
| Classifier: Programming Language :: Python :: 3.4 | ||
| Classifier: Programming Language :: Python :: 3.5 | ||
| Classifier: Topic :: Software Development | ||
| Classifier: Topic :: Software Development :: Testing |
+10
-12
@@ -13,15 +13,14 @@ from distutils.core import setup | ||
| Just import the ``assert_that`` function, and away you go: | ||
| Just import the ``assert_that`` function, and away you go:: | ||
| .. code-block:: python | ||
| from assertpy import assert_that | ||
| class TestSomething(object): | ||
| def test_something(self): | ||
| assert_that(1 + 2).is_equal_to(3) | ||
| assert_that('foobar').is_length(6).starts_with('foo').ends_with('bar') | ||
| def test_something(): | ||
| assert_that(1 + 2).is_equal_to(3) | ||
| assert_that('foobar').is_length(6).starts_with('foo').ends_with('bar') | ||
| assert_that(['a', 'b', 'c']).contains('a').does_not_contain('x') | ||
| Of course, ``assertpy`` works best with a python test runner | ||
| like `Nose <http://nose.readthedocs.org/>`_ | ||
| or `pytest <http://pytest.org/latest/contents.html>`_.""" | ||
| like `pytest <http://pytest.org/latest/contents.html>`_ (our favorite) | ||
| or `Nose <http://nose.readthedocs.org/>`_.""" | ||
@@ -37,3 +36,3 @@ setup(name = 'assertpy', | ||
| download_url = 'https://codeload.github.com/ActivisionGameScience/assertpy/tar.gz/%s' % assertpy.__version__, | ||
| keywords = ['test', 'testing', 'assert', 'assertion', 'assert_that', 'nose', 'nosetests', 'pytest'], | ||
| keywords = ['test', 'testing', 'assert', 'assertion', 'asserthat', 'assert_that', 'nose', 'nosetests', 'pytest'], | ||
| license = 'BSD', | ||
@@ -51,6 +50,5 @@ classifiers = [ | ||
| 'Programming Language :: Python :: 3', | ||
| 'Programming Language :: Python :: 3.3', | ||
| 'Programming Language :: Python :: 3.4', | ||
| 'Programming Language :: Python :: 3.5', | ||
| 'Topic :: Software Development', | ||
| 'Topic :: Software Development :: Testing'] | ||
| ) | ||
| 'Topic :: Software Development :: Testing']) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
45774
16.39%854
17.15%