Package: inxpect
Intuitive inspect and expectTable of ContentsInstallationFeaturesUsageDevelopmentInstallationSimply install it from pypi:pip install inxpector from sources:git clone git@github.com:apieum/inxpect.git
cd inxpect
python setup.py installFeaturesBuild properties inspector from a type or objectCan inspect objects recursively (default depth is 0)Defines a set of chainable expectations within properties typesProvides a set of operatorsEach operator support is_true and is_false methodsCan inject getters to operators and expectations constructorsCan inject getter closures to expectationsGetters, operators and expectations are serializables and searchablesExpectations results are tunables (returns booleans, raises exceptions, log…)tested with python versions 2.7, 3.2 and 3.3Chainable expectationsDefaults:equal_to, not_equal_to, same_as, not_same_aslower_than, lower_or_equal_than, greater_than, greater_or_equal_thantype_is, type_is_not, instance_of, not_instance_oflen, and, oroperators symbols: ==, !=, >, >=, <, <=, ‘|’ (for or), ‘&’ (for and)Lists and Tuples:has, has_all, has_anyhas_key, has_keys, has_any_keyatDict:has_item, has_items, has_any_itemhas_key, has_keys, has_any_keyhas_value, has_values, has_any_valueatOperatorsEqual, NotEqual, SameAs, NotSameAsLowerThan, LowerOrEqualThan, GreaterThan, GreaterOrEqualThanTypeIs, TypeIsNot, InstanceOf, NotInstanceOfContains, NotContains, ContainsAny, ContainsAllContainsItem, ContainsAllItem, ContainsAnyItemContainsValue, ContainsAllValue, ContainsAnyValueQuick examplefrominxpect.expect.operatorimportEqualfrominxpectimportAnd,Orequal_2=Equal(2)assertequal_2(2)assertequal_2(3)isFalseassertEqual.is_true(2,2)assertEqual.is_false(2,3)# You can use a getter function:mod_3=lambdanum:num%3multiple_of_3=Equal(0,mod_3)assertmultiple_of_3(15)assertmultiple_of_3(16)isFalse# chaining:mod_5=lambdanum:num%5multiple_of_5=Equal(0,mod_5)multiple_of_3_and_5=And(multiple_of_3,multiple_of_5)multiple_of_3_or_5=Or(multiple_of_3,multiple_of_5)assertmultiple_of_3_and_5(15)assertmultiple_of_3_or_5(16)isFalsemultiple_of_4=Equal(0,lambdanum:num%4)multiple_of_3_4_or_5=multiple_of_3_or_5|multiple_of_4assertmultiple_of_3_4_or_5(16)# As multiple_of_3_or_5 is Or chain multiple_of_4 is just appendedassertmultiple_of_3_4_or_5ismultiple_of_3_or_5# With And a new And chain is returned:assert(multiple_of_3_or_5ismultiple_of_3_or_5&multiple_of_4)isFalse# Testing and search (lambda is partially pickled):assert(multiple_of_5==Equal(0,lambdanum:num%5))# Comparison is made on bytecodeassert(multiple_of_5==Equal(0,lambdanum:num%4))isFalse# Comparison is made on arguments (and their name)assert(multiple_of_5==Equal(0,lambdanum,*args:num%5))isFalseUsageForewordsEach example uses these 2 weird classes:classSubject(object):args=tuple()kwargs=dict()def__call__(self,event):self.args=event.argsself.kwargs=event.kwargsevent.result=FalseclassEventData(object):name='event'subject=Subject()args=tuple()kwargs=dict()result=Truedef__init__(self,**kwargs):forattr,valueinkwargs.items():setattr(self,attr,value)InspectNothing hard, just a function “expect_factory” wich take an object or a type as template
and returns an inspector wich contains properties named like ones of the given template.
Inspector properties are operations which helps to make expectations on objects
with same properties (name, and expected type) as template.“expect_factory” takes an optional second argument (by default 0) to precise the depth of recursion.
Each property containing an object will be replaced by an inspector until depth, otherwise,
object become an “ExpectSame” object.importinxpectexpect=inxpect.expect_factory(EventData)asserthasattr(expect,'result')asserthasattr(expect.subject,'args')isFalse# with depth to 1:expect=inxpect.expect_factory(EventData,1)asserthasattr(expect.subject,'args')Expect Basicsimportinxpectexpect=inxpect.expect_factory(EventData)name_is_event1=expect.name.equal_to('event1')# can be done with ==result_is_not_None=expect.result!=Noneis_event1=name_is_event1&result_is_not_Noneevent1=EventData(name='event1')event2=EventData(name='event2',result=None)assertresult_is_not_None(event1)assertresult_is_not_None(event2)isFalseassertname_is_event1(event1)assertname_is_event1(event2)isFalselog=[]expected='Name%sis not "event1"'defis_event1_fails(chain,at,*args,**kwargs):# args and kwargs are same passed to is_event1:event=args[0]ifatinexpect.name.equal_to('event1'):log.append(expected%event.name)returnFalseis_event1.on_fail(is_event1_fails)assertis_event1(event1)assertis_event1(event2)isFalseassertlog[0]==expected%'event2'Expect ClosuresAs briefly seen, operators can take a getter argument.
That’s what is done by inspect that use AttrByName getter to return an attribute by its name.In addition to this, each assertion (equal_to…) can take a closure argument,
wich is a function that take getter result as argument and return the value to test.Internally, this mecanism is used to provide ‘at’ and ‘len’ assertions,
the example above illustrate how ‘len’ functions.importinxpectexpect=inxpect.expect_factory(EventData)name_len_is_5=expect.name.equal_to(5,len)event1=EventData()event2=EventData(name='0123456789')assertname_len_is_5(event1)assertname_len_is_5(event2)isFalse# the closure also encapsulate len for comparisonsassertname_len_is_5==expect.name.equal_to(5,len)### Details:# Never use this but it shows how len is encapsulated# func is the internal varname, needed to compare bytecodefunc=len# Lambda permit to get bytecode from __code__ attibutefunc_len=lambda*args,**kwargs:func(*args,**kwargs)not_func_len=lambda*args,**kwargs:len(*args,**kwargs)assert(name_len_is_5==expect.name.equal_to(5,func_len))# assert it compares bytecodeassert(name_len_is_5==expect.name.equal_to(5,not_func_len))isFalseExpect ShouldIn case default operators are not sufficient, you can define yours and use them easily with should.
An operator is a simple class that extends Operator (located in inxpect.expect.operator)
which need the class method “is_true(cls, given, expected)” to returns a boolean.frominxpect.expectimportExpectfrominxpect.expect.operatorimportOperatorclassMultipleOf5(Operator):@classmethoddefis_true(cls,given,expected):returngiven%5==expectedexpect=Expect()expected=0multiple_of_5=expect.should(MultipleOf5,expected)assertmultiple_of_5(10)assertmultiple_of_5(11)isFalse# Should provides also the negation:not_multiple_of_5=expect.should_not(MultipleOf5,expected)assertnot_multiple_of_5(11)# Should can take closure:divide_by_2=lambdagiven:given/2multiple_of_2_and_5=expect.should(MultipleOf5,expected,divide_by_2)assertmultiple_of_2_and_5(10)assertmultiple_of_2_and_5(15)isFalse# Should syntactic sugar:is_10=expect.should==10assertis_10(10)assertis_10(11)isFalse# like expect:is_10=expect==10assertis_10(10)assertis_10(11)isFalse# at the difference that should can take a closure:mod_2=lambdanum:num%2multiple_of_2=expect.should==(0,mod_2)assertmultiple_of_2(10)assertmultiple_of_2(11)isFalse# unlike expect:weird_example=expect==(0,mod_2)assertweird_example(10)isFalseassertweird_example((0,mod_2))DevelopmentFell free to give feedback or improvements.Launch test:git clone git@github.com:apieum/inxpect.git
cd inxpect
nosetests --with-spec --spec-color