[Python-ideas] Overloading operators for testing

Steven D'Aprano steve at pearwood.info
Tue Sep 20 10:12:23 EDT 2016


On Sun, Sep 18, 2016 at 02:52:31AM +0200, Arek Bulski wrote:
> I am using declarative testing a lot and I found out why unit tests are so
> clunky.

I don't think unit tests are clunky.


> The reason why assertEquals(a,b) is used is because if we put
> `assert a==b` then nose can catch the AssertionError but wont find out what
> was returned or expected. 

That's not the only reason.

assert* methods can take an arbitrary number of arguments. == cannot.

It is easy to extend the collection of assert* methods, to use 
inheritance to modify them, and so on. Not so easy if you have only a 
single, global function or operator.

assert* methods can run even when assertions are disabled.


> This could be easily overcome if we allow
> oveloading == operator from outside. Right now == would have to be changed
> for every lefhand object that is compared in the tests, builtin types
> including. We could use a way to change it from above, so to speak.

Having global state that controls the behaviour of == is not a good 
idea. Look at your proposed code:

> Consider this:
> 
> def __glob_eq__(a,b):
>   if not a == b:
>       raise FoundInequalityError(a,b)
>   return True
> 
> assert obj1 == obj2   #<-- using eq above

That can't work as you have written it, because it will recurse forever. 
obj1 == obj2 will call the global eq, which calls == which calls the 
global eq, which calls == which calls the global eq. 

So let's re-write it:

def __glob_eq__(a,b):
    saved_state = get_global_eq()
    delete_global_eq()
    try:
        if not a == b:  # calls the real ==
            raise FoundInequalityError(a,b)
        return True
    finally:
        set_global_eq(saved_state)


Okay, that's pretty yucky, and its not thread-safe, but at least its 
only in one place, right? Except it isn't. Every single call to == (and 
!= ) will have to be re-written to do the same thing.

And of course, that just replaces assertEquals. What about all the other 
assert* methods?

But there's another, more fundamental problem with using assert in this 
way. You cannot run your unit tests with assertions turned off.

That is why I think the nose style of using "assert a == b" for unit 
testing is seriously broken. That is an abuse of the assert statement. 
It's good enough for a handful of quick and dirty tests, but is not good 
enough for a full-sized unit test suite.


-- 
Steve


More information about the Python-ideas mailing list