unittest: Proposal to add failUnlessNear

Antoon Pardon apardon at forel.vub.ac.be
Tue Jul 20 12:07:19 CEST 2004


Piggy backing.

Op 2004-07-19, John Roth schreef <newsgroups at jhrothjr.com>:

> "Bengt Richter" <bokr at oz.net> wrote in message
> news:cdh0mg$npj$0 at 216.39.172.122@theriver.com...
>> On 19 Jul 2004 11:06:47 GMT, Antoon Pardon <apardon at forel.vub.ac.be>
> wrote:
>>
>> >I have been working with unittests lately and found that the
>> >self.failUnlessAlmostEqual, isn't as usefull as it could be.
>> >
>> >My main problem is, that it is only usefull with objects
>> >that can be converted to floats, while there are a whole
>> >bunch of objects that can be almost equal but not so
>> >convertable. The first example coming to mind being
>> >complex numbers.
>> >
>> >A secondary objection is that you are limited to
>> >a very specific set of tolerances. If for instance
>> >you want to verify that two numbers differ at most
>> >by 0.0003 you can't specify that.
>> >
>> >So I propose to add the following
>> >
>> >
>> >  def failUnlessNear(self, first, second, tolerance=1e-7, msg=None, norm=abs):
>> >      """Fail if the two objects are too far appart as determined
>> >         by the distance between them and the tolerance allowed.
>> >      """
>> >      if norm(second-first) > tolerance:
>> >          raise self.failureException, \
>> >                (msg or '%s != %s within %s tolerance' % (`first`, `second`, `tolerance`))
>> >
>> >
>> >-- 
>> >Antoon Pardon
>>
>> How about a more general solution? E.g., how about an optional keyword arg
>>     cmp=comparison_function

I think the ability to define a norm, being the length of the number,
vector or other entities is all that is needed. The reason I think so is that
mathematcally, the norm of a vector and the distance between two vectors
are closely linked. The norm of a vector is the distance between this
vector and the null vector and the distance between two vectors is the
norm of the difference vector.

We could of course leave this relationship behind, but then I think
providing a distance function is enough, something like the following
should suffice.

  def failUnlessNear(self, first, second, tolerance=1e-7, msg=None, 
                     distance=lambda x,y :abs(x-y)):
      """Fail if the two objects are too far appart as determined
         by the distance between them and the tolerance allowed.
      """
      if distance(second,first) > tolerance:
          raise self.failureException, \
                (msg or '%s != %s within %s tolerance' % (`first`, `second`, `tolerance`))

>> passed to failUnlessAlmostEqual? (I'm guessing here, no time to look at it)
>> E.g., that way for mixed numbers including complex you could (if you thought
>> it made sense, which I am not necessarily arguing ;-) use e.g.
>>
>>     def special_cmp(x,y): # untested!
>>         diff = complex(x)-complex(y)
>>         return max(cmp(abs(diff.real), tolerance), cmp(abs(diff.imag), tolerance))
>>
>> and pass cmp=special_cmp as the kwarg.

I'm sorry but I have a hard time figuring out what you want to do here.
First of all, your special_cmp normally has no access to the tolerance.
So either you want a third parameter or you are mixing things that
should go in special_cmp with things you want to happen in
failUnlessNear. Using my distance approach, I think you wanted this:

  def dist(x,y):

    diff = complex(x)-complex(y)
    return max(abs(diff.real) , abs(diff.imag))


>> For special objects, you could define other kinds of nearness, and raise
>> appropriate informative exceptions if you get non-comparable arguments.

Well I have a hard time imaganing a usefull distance function whose
results don't form a total order. If I have a tolerance t and an
actual distance d, but d and t don't compare then the question whether
the two objects are near enough is unanswerable.

>> (obviously tolerance has to be defined, so if you want to vary it conveniently,
>> you could pass it to a factory function that does the above def and  returns
>> it with tolerance captured in a closure referred to by special_cmp).
>>
>> Or some other way. The point is you get to define the cmp= function as you please
>> without modifying the framework (once the optional kwarg is implemented)

Well mostly I'm all for choosing the more general solution. But in this
case I sure would like to know if you have an actual example in mind.

The reason I ask is that this is stuff that comes from mathematics and
this is AFAIK a general framework that is used for distances on all
kind of mathematical objects. I can of course be ignorant of a more
general framework in mathematics to deal with this and of course it
is possible that a more general approach is usefull although it hasn't
caught the interest of the mathematical community, that is why I ask
for an example.

-- 
Antoon Pardon



More information about the Python-list mailing list