robber-1.1.5/0000755000076500000240000000000013576641416013221 5ustar adminstaff00000000000000robber-1.1.5/LICENSE0000644000076500000240000000212013576633403014216 0ustar adminstaff00000000000000MIT License Copyright (C) 2012-2016 Veselin Todorov (hi@vesln.com) & Tao Liang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. robber-1.1.5/MANIFEST.in0000644000076500000240000000006613576633403014756 0ustar adminstaff00000000000000include LICENSE include MANIFEST.in include README.md robber-1.1.5/PKG-INFO0000644000076500000240000000127113576641416014317 0ustar adminstaff00000000000000Metadata-Version: 1.1 Name: robber Version: 1.1.5 Summary: BDD / TDD assertion library for Python Home-page: https://github.com/vesln/robber.py Author: Tao Liang Author-email: tao@synapse-ai.com License: UNKNOWN Description: BDD / TDD assertion library for Python Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing robber-1.1.5/README.md0000644000076500000240000001462613576633403014506 0ustar adminstaff00000000000000[![Build Status](https://secure.travis-ci.org/taoenator/robber.py.png)](http://travis-ci.org/taoenator/robber.py) [![Coverage Status](https://coveralls.io/repos/github/taoenator/robber.py/badge.svg?branch=master)](https://coveralls.io/github/taoenator/robber.py?branch=master) [![Code Climate](https://codeclimate.com/github/vesln/robber.py/badges/gpa.svg)](https://codeclimate.com/github/vesln/robber.py) # robber.py - BDD / TDD assertion library for Python. ## Synopsis In order to use `robber`, you need to import `expect` from the module: ```python from robber import expect ``` That's all. You are good to go. ### Assertions #### eq/== Asserts that actual is equal (==) to expected: ```python expect(1).to.eq(1) expect([1, 2]).to.eq([1, 2]) ``` Also: ```python expect(1) == 1 ``` #### ne/!= Asserts that actual is not equal (!=) to expected: ```python expect(1).to.ne(2) expect(1).to != 2 expect(1) != 2 ``` #### equal Asserts that the target is identical (is) to the expected: ```python expect(1).to.equal(1) ``` #### true Asserts that the target is True: ```python expect(True).to.be.true() ``` #### false Asserts that the target is False: ```python expect(False).to.be.false() ``` #### instanceof Asserts that the target is an instance of expected: ```python expect(obj).to.be.instanceof(Klass) ``` #### match Asserts that the target can be matched by a regular expression: ```python expect('foo').to.match(r'foo') ``` #### respond_to Asserts that the target responds to a method: ```python expect(obj).to.respond_to('method') ``` #### truthy Asserts that the target is truthy: ```python expect(['test']).to.be.truthy() ``` #### falsy Asserts that the target is falsy: ```python expect([]).to.be.falsy() ``` #### length Asserts that the target has a length of expected: ```python expect([1, 2]).to.have.length(2) expect('str').to.have.length(3) ``` #### empty Asserts that the target is empty: ```python expect([]).to.be.empty() expect('').to.be.empty() ``` #### string Asserts that the target is a string: ```python expect('str').to.be.a.string() ``` #### integer Asserts that the target is an integer: ```python expect('str').to.be.an.integer() ``` #### float Asserts that the target is floating point number: ```python expect(1.0).to.be.a.float() ``` #### list Asserts that the target is a list: ```python expect([1, 2]).to.be.a.list() ``` #### dict Asserts that the target is a dictionary: ```python expect({}).to.be.a.dict() ``` #### tuple Asserts that the target is a tuple: ```python expect((1, 2)).to.be.a.tuple() ``` #### none Asserts that the target is None: ```python expect(None).to.be.none() ``` #### above Asserts that the target is above expected: ```python expect(2).to.be.above(1) ``` #### below Asserts that the target is below expected: ```python expect(1).to.be.below(2) ``` #### within Asserts that the target is within expected: ```python expect(2).to.be.within(0, 2) ``` #### contain Asserts that the target contains an element, or a key: ```python expect([1,2,3]).to.contain(1, 2, 3) expect({'foo': 'bar', 'foo1': 'bar1'}).to.contain('foo', 'foo1') ``` #### exclude Asserts that the target does not contain an element, or a key: ```python expect({'foo': 'bar'}).to.exclude('baz') ``` #### throw Asserts that the target throws an exception (or its subclass) ```python expect(lambda: raise_exception(...)).to.throw(Exception) expect(lambda: raise_exception(...)).to.throw(ParentException) expect(any_callable).to.throw(Exception) expect(any_callable).to.throw(ParentException) ``` #### throw_exactly Asserts that the target throws exactly an exception (not its subclass) ```python expect(lambda: raise_exception(...)).to.throw_exactly(Exception) expect(any_callable).to.throw_exactly(Exception) ``` #### called Asserts that a mock has been called ```python expect(mock).to.be.called() ``` #### called_once Asserts that a mock has been called exactly one time ```python expect(mock).to.be.called_once() ``` #### callable Asserts that a object is callable ```python expect(object).to.be.callable() ``` #### called_with Asserts that a mock has been called with params ```python expect(mock).to.be.called_with(*args, **kwargs) ``` #### called_once_with Asserts that a mock has been called once with params ```python expect(mock).to.be.called_once_with(*args, **kwargs) ``` #### ever_called_with Asserts that a mock has ever been called with params. The call is not necessary to be to latest one (the same as assert.any_call). ```python expect(mock).to.have.been.ever_called_with(*args, **kwargs) expect(mock).to.have.any_call(*args, **kwargs) ``` ### Language chains In order to write more readable assertions, there are a few built-in language chains that you can use: #### Positive chains - to - be - been - a - an - have #### Negative chains - not_to For example, the following two lines are functionally equivalent: ```python expect(1.0).to.be.a.float() expect(1.0).float() ``` ### Expectation chaining In the spirit of more readable assertions, and to eliminate redundant evaluations of the same expression, you can chain multiple expectations. For example, the following two lines are functionally equivalent. The first example evaluates the expression '1 + 1' only once: ```python expect(1 + 1).to.be.an.integer().to.be.within(1, 3) expect(1 + 1).to.be.an.integer() expect(1 + 1).to.be within(1, 3) ``` ### Custom assertions Writing custom assertion is as easy as extending a base matcher class and adding the method `matches` for matching and the property `explanation` for the error notice: ```python class Chain(Base): def matches(self): expectation = self.actual(None) chain = getattr(expectation, self.expected) return expectation is chain @property def explanation(self): return Explanation(self.actual, self.is_negative, 'have chain', self.expected) expect.register('chain', Chain) ``` After you register the new matcher, you can use it as expected: ```python expect(obj).to.have.chain('be') ``` ### Custom error messages If you want to have custom explanations, for assertion or group of assertions, you can simply do: ```python from robber import CustomExplanation with CustomExplanation('Something went wrong'): expect(1).to.eq(2) ``` ## Installation ```bash $ pip install robber ``` ## Requirements - Python 2.6, 2.7, 3.5 or 3.6 - pip - nose (for testing) ## Tests ```bash $ nosetests tests/ ``` ## License MIT License robber-1.1.5/robber/0000755000076500000240000000000013576641416014474 5ustar adminstaff00000000000000robber-1.1.5/robber/__init__.py0000644000076500000240000000060013576633670016604 0ustar adminstaff00000000000000# Do not change the importing order in the next 4 lines. Doing so may break everything. from robber.custom_explanation import CustomExplanation # noqa F401 from robber.expect import expect # noqa F401 from robber.bad_expectation import BadExpectation # noqa F401 import robber.matchers # noqa F401 __all__ = [ 'custom_explanation', 'expect', 'bad_expectation', 'matchers', ] robber-1.1.5/robber/bad_expectation.py0000644000076500000240000000046013576633403020174 0ustar adminstaff00000000000000from termcolor import colored class BadExpectation(Exception): """ Raised when an assertion fails. """ def __init__(self, message): super(BadExpectation, self).__init__(message) self.message = message def __str__(self): return colored(self.message, 'red') robber-1.1.5/robber/constants.py0000644000076500000240000000004513576633403017056 0ustar adminstaff00000000000000TENSE_WORDS = ['be', 'have', 'been'] robber-1.1.5/robber/custom_explanation.py0000644000076500000240000000060013576633403020753 0ustar adminstaff00000000000000from robber.expect import expect class CustomExplanation: """ with CustomExplanation('Custom failure message'): # some assertions """ def __init__(self, message): self.message = message def __enter__(self, *args, **kwargs): expect.fail_with(self.message) def __exit__(self, *args, **kwargs): expect.remove_custom_message() robber-1.1.5/robber/expect.py0000644000076500000240000000344613576633403016342 0ustar adminstaff00000000000000class expect: """ The main entry point of the library. Most of the time you don't have to use anything but this class. If you want to register custom matchers, you have to register them to `expect`: ``` expect.register(name, klass_that_inherits_from_matchers_base) ``` In order to make the tests more readable, there are a few chains: ``` to, be, a, an, have, been ``` """ matchers = {} message = None def __init__(self, obj): self.obj = obj self.not_to_flag = False self.__setup_chaining() @classmethod def fail_with(cls, message): cls.message = message @classmethod def remove_custom_message(cls): cls.message = None @classmethod def register(cls, name, klass, is_negative=False): cls.matchers[name] = klass def method(self, other=None, *args, **kwargs): if self.not_to_flag: negative_fact = not is_negative else: negative_fact = is_negative return klass(self.obj, other, negative_fact, *args, **kwargs).fail_with(self.message).match() setattr(cls, name, method) @classmethod def matcher(cls, name): return cls.matchers[name] @classmethod def unregister(cls, name): delattr(cls, name) pass @classmethod def registered(cls, name): try: getattr(cls, name) except AttributeError: return False else: return True @property def not_to(self): self.not_to_flag = not self.not_to_flag return self def __setup_chaining(self): self.to = self self.be = self self.been = self self.a = self self.an = self self.have = self robber-1.1.5/robber/explanation.py0000644000076500000240000001073413576633403017372 0ustar adminstaff00000000000000import re from robber.constants import TENSE_WORDS class Explanation: """ This class gives use a simple way to implement explanations for your expectations. """ def __init__( self, actual=object, is_negative=False, action=None, expected=object, another_action=None, another_expected=object, more_detail=None, negative_action=None, force_disable_repr=False, need_to_build_diffs=False, other=object ): """ :param actual: first object :param is_negative: is this a negative explanation? :param action: a string defining an action, ex: "equal", "be True", "called"... :param expected: second object :param another_expected: third object :param more_detail: custom message that will be put right after the expectation sentence :param negative_action: the action used in negative explanation, ex: "be False", "exclude"... :param another_action: the action applied on the third object :param force_disable_repr: force disable the use of repr() :param need_to_build_diffs: trigger if you want to build the diffs string :param other: additional object ex: Expected A to be called with B but actually called with Z """ self.actual = actual self.action = action self.expected = expected self.another_expected = another_expected self.other = other self.force_disable_repr = force_disable_repr self.is_negative = is_negative self.action = negative_action if is_negative and negative_action else action self.negative_word = ' not' if is_negative and not negative_action else '' self.another_action = ' {0}'.format(another_action) if another_action else '' self.more_detail = self.build_more_detail(more_detail) self.expected_word = ' B' if self.is_passed(expected) else '' self.another_expected_word = ' C' if self.is_passed(another_expected) else '' self.other_word = ' Z' if self.is_passed(other) else '' def build_more_detail(self, info): if not self.is_passed(self.other) and not info: return '' if self.is_negative: return 'But it happened\n' if info: return '{0}\n'.format(info) if self.is_passed(self.other): tense_words_str = ' |'.join(TENSE_WORDS) match = re.search('({0}|)(.+)'.format(tense_words_str), self.action) other_action = '{action}{another_action} '.format( action=match.group(2), another_action=self.another_action ) return 'Actually {0}Z\n'.format(other_action) @property def is_repr(self): having_two_strings = True if type(self.actual) is str and type(self.expected) is str else False return False if self.force_disable_repr else not having_two_strings @property def message(self): actual_line = self.build_line(self.actual, 'A', self.is_repr) expected_line = self.build_line(self.expected, 'B', self.is_repr) another_expected_line = self.build_line(self.another_expected, 'C', self.is_repr) other_line = self.build_line(self.other, 'Z', self.is_repr) return ( '\n' '{actual_line}' '{expected_line}' '{another_expected_line}' '{other_line}' 'Expected A{negative_word} to {action}{expected_word}{another_action}{another_expected_word}\n' '{more_detail}' ).format( actual_line=actual_line, expected_line=expected_line if self.is_passed(self.expected) else '', another_expected_line=another_expected_line if self.is_passed(self.another_expected) else '', other_line=other_line if not self.is_negative and self.is_passed(self.other) else '', negative_word=self.negative_word, action=self.action, expected_word=self.expected_word, more_detail=self.more_detail, another_action=self.another_action, another_expected_word=self.another_expected_word, ) @staticmethod def build_line(obj, obj_name, is_repr): if Explanation.is_passed(obj): if is_repr: obj = repr(obj) return '{obj_name} = {obj}\n'.format( obj_name=obj_name, obj=obj ) return '' @staticmethod def is_passed(param): return False if param is object else True robber-1.1.5/robber/matchers/0000755000076500000240000000000013576641416016302 5ustar adminstaff00000000000000robber-1.1.5/robber/matchers/__init__.py0000644000076500000240000000234713576633403020416 0ustar adminstaff00000000000000__all__ = [ 'boolean', 'callable', 'called', 'called_once', 'called_once_with', 'called_with', 'contain', 'equal', 'ever_called', 'exception', 'identical', 'instanceof', 'length', 'numbers', 'regexp', 'respond_to', 'truthy', 'types', ] from robber.matchers.boolean import * # noqa F403 from robber.matchers.callable import * # noqa F403 from robber.matchers.called import * # noqa F403 from robber.matchers.called_once import * # noqa F403 from robber.matchers.called_once_with import * # noqa F403 from robber.matchers.called_with import * # noqa F403 from robber.matchers.contain import * # noqa F403 from robber.matchers.equal import * # noqa F403 from robber.matchers.ever_called_with import * # noqa F403 from robber.matchers.exception import * # noqa F403 from robber.matchers.identical import * # noqa F403 from robber.matchers.instanceof import * # noqa F403 from robber.matchers.length import * # noqa F403 from robber.matchers.numbers import * # noqa F403 from robber.matchers.regexp import * # noqa F403 from robber.matchers.respond_to import * # noqa F403 from robber.matchers.truthy import * # noqa F403 from robber.matchers.types import * # noqa F403 robber-1.1.5/robber/matchers/base.py0000644000076500000240000000162313576633403017565 0ustar adminstaff00000000000000from robber.bad_expectation import BadExpectation class Base: """ Base matcher. All matchers inherit from it. If you want to create a custom matcher, it's a good idea to extend it, as well. """ def __init__(self, actual, expected=None, is_negative=False, *args, **kwargs): self.actual = actual self.expected = expected self.is_negative = is_negative self.args = args self.kwargs = kwargs self.message = None self.fail_class = None def fail_with(self, message): self.message = message return self def match(self): if self.matches() is not self.is_negative: return True message = self.message or self.explanation.message raise BadExpectation(message) @property def negative_message(self): if self.is_negative: return ' not' return '' robber-1.1.5/robber/matchers/boolean.py0000644000076500000240000000075413576633403020276 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Boolean(Base): """ expect(true).to.be.true() expect(true).to.be.false() """ def matches(self): return self.actual is True @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be True', negative_action='be False') expect.register('true', Boolean) expect.register('false', Boolean, is_negative=True) robber-1.1.5/robber/matchers/callable.py0000644000076500000240000000061713576633403020414 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Callable(Base): """ expect(object).to.be.callable() """ def matches(self): return callable(self.actual) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be callable') expect.register('callable', Callable) robber-1.1.5/robber/matchers/called.py0000644000076500000240000000100113576633403020065 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Called(Base): """ expect(mock).to.be.called() """ def matches(self): try: return self.actual.called except AttributeError: raise TypeError('{actual} is not a mock'.format(actual=self.actual)) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be called') expect.register('called', Called) robber-1.1.5/robber/matchers/called_once.py0000644000076500000240000000120213576633403021074 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class CalledOnce(Base): """ expect(mock).to.be.called_once() """ def matches(self): try: return self.actual.call_count == 1 except AttributeError: raise TypeError('{actual} is not a mock'.format(actual=self.actual)) @property def explanation(self): return Explanation( self.actual, self.is_negative, 'be called once', more_detail='Called {0} times'.format(self.actual.call_count) ) expect.register('called_once', CalledOnce) robber-1.1.5/robber/matchers/called_once_with.py0000644000076500000240000000237713576633403022145 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base from robber.matchers.mock_mixin import MockMixin class CalledOnceWith(Base, MockMixin): """ expect(mock).to.be.called_once_with(*args, **kwargs) """ def matches(self): try: called_once = self.actual.call_count == 1 called_with = self.actual.call_args == self.call_args except AttributeError: raise TypeError('{actual} is not a mock'.format(actual=self.actual)) else: return called_once and called_with @property def explanation(self): if not self.actual.called: return Explanation( self.actual, self.is_negative, 'be called once with', self.expected_args_str, more_detail='Actually not called', force_disable_repr=True ) additional_info = 'Actually called {call_count} times with Z'.format(call_count=self.actual.call_count) return Explanation( self.actual, self.is_negative, 'be called once with', self.expected_args_str, other=self.call_args_str, more_detail=additional_info, force_disable_repr=True ) expect.register('called_once_with', CalledOnceWith) robber-1.1.5/robber/matchers/called_with.py0000644000076500000240000000176213576633403021136 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base from robber.matchers.mock_mixin import MockMixin class CalledWith(Base, MockMixin): """ expect(mock).to.be.called_with(*args, **kwargs) """ def matches(self): try: return self.actual.called and self.actual.call_args == self.call_args except AttributeError: raise TypeError('{actual} is not a mock'.format(actual=self.actual)) @property def explanation(self): if not self.actual.called: return Explanation( self.actual, self.is_negative, 'be called with', self.expected_args_str, more_detail='Actually not called', force_disable_repr=True ) return Explanation( self.actual, self.is_negative, 'be called with', self.expected_args_str, other=self.call_args_str, force_disable_repr=True ) expect.register('called_with', CalledWith) robber-1.1.5/robber/matchers/contain.py0000644000076500000240000000213313576633403020303 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Contain(Base): """ expect({'key': value}).to.contain('key 1', 'key 2', 'key n') expect([1, 2, 3]).to.contain(1, 2, 3) """ def matches(self): expected_list = list(self.args) expected_list.insert(0, self.expected) self.expected_arg = None if not self.is_negative: for expected in expected_list: if expected not in self.actual: self.expected_arg = expected break else: for expected in expected_list: if expected in self.actual: self.expected_arg = expected break existed = self.expected_arg is not None return existed is self.is_negative @property def explanation(self): return Explanation(self.actual, self.is_negative, 'contain', self.expected_arg, negative_action='exclude') expect.register('contain', Contain) expect.register('exclude', Contain, is_negative=True) robber-1.1.5/robber/matchers/equal.py0000644000076500000240000000744113576633403017766 0ustar adminstaff00000000000000from difflib import Differ from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base try: from collections import OrderedDict except ImportError: # pragma: no cover ordered_dict_available = False else: ordered_dict_available = True class Equal(Base): """ expect(1).to.eq(1) expect(1) == 1 expect(1).to.ne(1) expect(1) != 1 """ def matches(self): self.standardize_args() return self.actual == self.expected @property def explanation(self): types = [dict, list, str] diffs = ['dict_diffs', 'list_diffs', 'str_diffs'] more_detail = None for t, d in zip(types, diffs): if type(self.actual) is t and type(self.expected) is t: more_detail = getattr(self, d) if ordered_dict_available: if type(self.actual) in (OrderedDict, dict) and type(self.expected) in (OrderedDict, dict): more_detail = self.dict_diffs return Explanation(self.actual, self.is_negative, 'equal', self.expected, more_detail=more_detail) @property def dict_diffs(self): a_keys = self.actual.keys() b_keys = self.expected.keys() for key in a_keys: if key not in b_keys: return "A has key '%s' while B does not" % key for key in b_keys: if key not in a_keys: return "B has key '%s' while A does not" % key for k in self.actual: val_a = self.actual[k] val_b = self.expected[k] if not val_a == val_b: return ( 'Diffs:\n' 'A[\'{key}\'] = {val_a}\n' 'B[\'{key}\'] = {val_b}\n' ).format(key=k, val_a=val_a, val_b=val_b) @property def list_diffs(self): if len(self.actual) != len(self.expected): return 'A and B do not have the same length' for m in self.actual: if m not in self.expected: return "A contains {m} while B does not".format(m=m) @property def str_diffs(self): if self.actual == self.expected: return '' differ = Differ() diffs = differ.compare(self.actual.splitlines(), self.expected.splitlines()) return 'Diffs:\n{0}'.format('\n'.join(diffs)) def standardize_args(self): self.actual = self.unicode_to_str(self.actual) self.expected = self.unicode_to_str(self.expected) if ordered_dict_available: if type(self.actual) is OrderedDict: self.actual = dict(self.actual) if type(self.expected) is OrderedDict: self.expected = dict(self.expected) @classmethod def unicode_to_str(cls, obj): try: if type(obj) is unicode: return cls._unicode_string_to_str(obj) except NameError: pass if type(obj) is list: return cls._unicode_list_to_str_list(obj) if type(obj) is dict: return cls._unicode_dict_to_str_dict(obj) return obj @classmethod def _unicode_string_to_str(cls, u_string): return u_string.encode('utf-8') @classmethod def _unicode_list_to_str_list(cls, u_list): str_list = list(u_list) for i in range(0, len(str_list)): str_list[i] = cls.unicode_to_str(str_list[i]) return str_list @classmethod def _unicode_dict_to_str_dict(cls, u_dict): str_dict = dict(u_dict) for key in str_dict: str_dict[key] = cls.unicode_to_str(str_dict[key]) return str_dict expect.register('eq', Equal) expect.register('__eq__', Equal) expect.register('ne', Equal, is_negative=True) expect.register('__ne__', Equal, is_negative=True) robber-1.1.5/robber/matchers/ever_called_with.py0000644000076500000240000000152413576633403022153 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base from robber.matchers.mock_mixin import MockMixin class EverCalledWith(Base, MockMixin): """ expect(mock).to.have.been.ever_called_with(*args, **kwargs) expect(mock).to.have.any_call(*args, **kwargs) """ def matches(self): try: return self.call_args in self.actual.call_args_list except AttributeError: raise TypeError('{actual} is not a mock'.format(actual=self.actual)) @property def explanation(self): return Explanation( self.actual, self.is_negative, 'have been ever called with', self.expected_args_str, force_disable_repr=True ) expect.register('ever_called_with', EverCalledWith) expect.register('any_call', EverCalledWith) robber-1.1.5/robber/matchers/exception.py0000644000076500000240000000262013576633403020647 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class ExceptionMatcher(Base): """ expect(lambda: call_something(with_some_params)).to.throw(any_exception) """ @property def raised(self): try: return self._raised except AttributeError: if not callable(self.actual): raise TypeError('{actual} is not callable'.format(actual=self.actual)) try: self.actual() except BaseException as raised: self._raised = raised return self._raised def matches(self): return isinstance(self.raised, self.expected) @property def verb(self): return 'be raised' @property def explanation(self): if self.raised: got = self.raised.__class__.__name__ else: got = 'nothing' return Explanation(self.expected.__name__, self.is_negative, self.verb, other=got) class ExactExceptionMatcher(ExceptionMatcher): """ expect(lambda: call_something(with_some_params)).to.throw_exactly(any_exception) """ def matches(self): return type(self.raised) == self.expected @property def verb(self): return 'be exactly raised' expect.register('throw', ExceptionMatcher) expect.register('throw_exactly', ExactExceptionMatcher) robber-1.1.5/robber/matchers/identical.py0000644000076500000240000000062113576633403020604 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Identical(Base): """ expect(1).to.equal(1) """ def matches(self): return self.actual is self.expected @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be', self.expected) expect.register('equal', Identical) robber-1.1.5/robber/matchers/instanceof.py0000644000076500000240000000070213576633403021001 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class InstanceOf(Base): """ expect(obj).to.be.an.instanceof(Klass) """ def matches(self): return isinstance(self.actual, self.expected) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be an instance of', self.expected) expect.register('instanceof', InstanceOf) robber-1.1.5/robber/matchers/length.py0000644000076500000240000000137313576633403020136 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Length(Base): """ expect('str').to.have.length(3) expect([1, 2, 3]).to.have.length(3) """ def matches(self): return len(self.actual) == self.expected @property def explanation(self): return Explanation(self.actual, self.is_negative, 'have length of', self.expected) class Empty(Base): """ expect('').to.be.empty() expect([]).to.be.empty() """ def matches(self): return len(self.actual) == 0 @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be empty') expect.register('length', Length) expect.register('empty', Empty) robber-1.1.5/robber/matchers/mock_mixin.py0000644000076500000240000000212013576633403021001 0ustar adminstaff00000000000000import re from mock import call class MockMixin: @property def call_args(self): if self.expected: return call(self.expected, *self.args, **self.kwargs) else: return call(*self.args, **self.kwargs) @property def expected_args_str(self): if not self.expected: return 'no arguments' expected_args_str = str(self.expected) if self.args: # Remove the parenthesis match = re.search('\((.*)\)', str(self.args)) args_str = match.group(1) expected_args_str = ', '.join((expected_args_str, args_str)) if self.kwargs: # Convert {'a': 1, 'b': 2} to 'a=1, b=2' kwargs_str = ', '.join(['{0}={1!r}'.format(k, v) for k, v in self.kwargs.items()]) expected_args_str = ', '.join((expected_args_str, kwargs_str)) return expected_args_str @property def call_args_str(self): match = re.search('call\((.*)\)', str(self.actual.call_args)) params = match.group(1) return params or 'no arguments' robber-1.1.5/robber/matchers/numbers.py0000644000076500000240000000412013576633403020321 0ustar adminstaff00000000000000from robber import BadExpectation from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Above(Base): """ expect(2).to.be.above(1) """ def matches(self): return self.actual > self.expected @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be above', self.expected) class Below(Base): """ expect(1).to.be.below(2) """ def matches(self): return self.actual < self.expected @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be below', self.expected) class Within(Base): """ expect(1).to.be.within(0, 2) """ def matches(self): return self.expected <= self.actual <= self.args[0] @property def explanation(self): return Explanation( self.actual, self.is_negative, 'be within', self.expected, 'and', self.args[0] ) class Change(Base): def __init__(self, callable, obj=None, is_negative=False, *args): self.callable = callable self.obj = obj self.args = args self.message = None self.is_negative = is_negative def match(self): return self def by(self, amount=0): self.changed = self.callable(self.obj) - self.obj self.amount = amount message = self.message or self.explanation.message if (self.changed == amount) == (not self.is_negative): return True raise BadExpectation(message) @property def explanation(self): return Explanation( self.callable.__name__, self.is_negative, 'change', self.obj, another_action='by', another_expected=self.amount, other=self.changed, force_disable_repr=True ) expect.register('above', Above) expect.register('below', Below) expect.register('more_than', Above) expect.register('less_than', Below) expect.register('greater_than', Above) expect.register('smaller_than', Below) expect.register('within', Within) expect.register('change', Change) robber-1.1.5/robber/matchers/regexp.py0000644000076500000240000000065613576633403020152 0ustar adminstaff00000000000000import re from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Match(Base): """ expect('foo').to.match(r'foo') """ def matches(self): return bool(re.match(self.expected, self.actual)) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'match', self.expected) expect.register('match', Match) robber-1.1.5/robber/matchers/respond_to.py0000644000076500000240000000074513576633403021033 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class RespondTo(Base): """ expect(obj).to.respond_to('method') """ def matches(self): return hasattr(self.actual, self.expected) and callable(getattr(self.actual, self.expected)) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'respond to', self.expected) expect.register('respond_to', RespondTo) robber-1.1.5/robber/matchers/truthy.py0000644000076500000240000000076613576633403020221 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class Truthy(Base): """ expect('str').to.be.truthy() expect('str').to.be.falsy() """ def matches(self): return bool(self.actual) @property def explanation(self): return Explanation(self.actual, self.is_negative, action='be truthy', negative_action='be falsy') expect.register('truthy', Truthy) expect.register('falsy', Truthy, is_negative=True) robber-1.1.5/robber/matchers/types.py0000644000076500000240000000407413576633403020022 0ustar adminstaff00000000000000from robber import expect from robber.explanation import Explanation from robber.matchers.base import Base class String(Base): """ expect('str').to.be.a.string() """ def matches(self): return isinstance(self.actual, str) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be a string') class Integer(Base): """ expect(1).to.be.a.integer() """ def matches(self): return isinstance(self.actual, int) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be an integer') class Float(Base): """ expect(1.0).to.be.a.float() """ def matches(self): return isinstance(self.actual, float) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be a floating point number') class List(Base): """ expect([]).to.be.a.list() """ def matches(self): return isinstance(self.actual, list) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be an array') class Dict(Base): """ expect({}).to.be.a.dict() """ def matches(self): return isinstance(self.actual, dict) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be a dictionary') class Tuple(Base): """ expect((1, 2)).to.be.a.tuple() """ def matches(self): return isinstance(self.actual, tuple) @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be a tuple') class Non(Base): """ expect(None).to.be.none() """ def matches(self): return self.actual is None @property def explanation(self): return Explanation(self.actual, self.is_negative, 'be None') expect.register('string', String) expect.register('integer', Integer) expect.register('float', Float) expect.register('list', List) expect.register('dict', Dict) expect.register('tuple', Tuple) expect.register('none', Non) robber-1.1.5/robber.egg-info/0000755000076500000240000000000013576641416016166 5ustar adminstaff00000000000000robber-1.1.5/robber.egg-info/PKG-INFO0000644000076500000240000000127113576641416017264 0ustar adminstaff00000000000000Metadata-Version: 1.1 Name: robber Version: 1.1.5 Summary: BDD / TDD assertion library for Python Home-page: https://github.com/vesln/robber.py Author: Tao Liang Author-email: tao@synapse-ai.com License: UNKNOWN Description: BDD / TDD assertion library for Python Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing robber-1.1.5/robber.egg-info/SOURCES.txt0000644000076500000240000000163713576641416020061 0ustar adminstaff00000000000000LICENSE MANIFEST.in README.md setup.cfg setup.py robber/__init__.py robber/bad_expectation.py robber/constants.py robber/custom_explanation.py robber/expect.py robber/explanation.py robber.egg-info/PKG-INFO robber.egg-info/SOURCES.txt robber.egg-info/dependency_links.txt robber.egg-info/requires.txt robber.egg-info/top_level.txt robber/matchers/__init__.py robber/matchers/base.py robber/matchers/boolean.py robber/matchers/callable.py robber/matchers/called.py robber/matchers/called_once.py robber/matchers/called_once_with.py robber/matchers/called_with.py robber/matchers/contain.py robber/matchers/equal.py robber/matchers/ever_called_with.py robber/matchers/exception.py robber/matchers/identical.py robber/matchers/instanceof.py robber/matchers/length.py robber/matchers/mock_mixin.py robber/matchers/numbers.py robber/matchers/regexp.py robber/matchers/respond_to.py robber/matchers/truthy.py robber/matchers/types.pyrobber-1.1.5/robber.egg-info/dependency_links.txt0000644000076500000240000000000113576641416022234 0ustar adminstaff00000000000000 robber-1.1.5/robber.egg-info/requires.txt0000644000076500000240000000001713576641416020564 0ustar adminstaff00000000000000mock termcolor robber-1.1.5/robber.egg-info/top_level.txt0000644000076500000240000000000713576641416020715 0ustar adminstaff00000000000000robber robber-1.1.5/setup.cfg0000644000076500000240000000011713576641416015041 0ustar adminstaff00000000000000[metadata] description-file = README.md [egg_info] tag_build = tag_date = 0 robber-1.1.5/setup.py0000644000076500000240000000210413576641247014732 0ustar adminstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os from setuptools import setup long_description = 'BDD / TDD assertion library for Python' if os.path.exists('README.rst'): long_description = open('README.rst').read() setup( name='robber', version='1.1.5', description='BDD / TDD assertion library for Python', long_description=long_description, author='Tao Liang', author_email='tao@synapse-ai.com', url='https://github.com/vesln/robber.py', packages=[ 'robber', 'robber.matchers', ], classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Testing' ], install_requires=[ 'mock', 'termcolor' ], tests_require=[ 'nose' ], )