Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #22275 > unrolled thread

unittest: assertRaises() with an instance instead of a type

Started byUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
First post2012-03-28 14:28 +0200
Last post2012-04-02 03:42 -0400
Articles 20 on this page of 23 — 9 participants

Back to article view | Back to comp.lang.python


Contents

  unittest: assertRaises() with an instance instead of a type Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-03-28 14:28 +0200
    Re: unittest: assertRaises() with an instance instead of a type Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-28 18:07 +0000
      Re: unittest: assertRaises() with an instance instead of a type Ben Finney <ben+python@benfinney.id.au> - 2012-03-29 12:55 +1100
        Re: unittest: assertRaises() with an instance instead of a type Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-29 06:35 +0000
        Re: unittest: assertRaises() with an instance instead of a type Peter Otten <__peter__@web.de> - 2012-03-29 08:55 +0200
        Re: unittest: assertRaises() with an instance instead of a type Steve Howell <showell30@yahoo.com> - 2012-03-28 22:50 -0700
      Re: unittest: assertRaises() with an instance instead of a type Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-03-29 09:08 +0200
        Re: unittest: assertRaises() with an instance instead of a type Peter Otten <__peter__@web.de> - 2012-03-29 09:48 +0200
        Re: unittest: assertRaises() with an instance instead of a type Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-30 02:45 +0000
          Re: unittest: assertRaises() with an instance instead of a type Ethan Furman <ethan@stoneleaf.us> - 2012-03-30 10:45 -0700
      Re: unittest: assertRaises() with an instance instead of a type Ethan Furman <ethan@stoneleaf.us> - 2012-03-29 08:35 -0700
        Re: unittest: assertRaises() with an instance instead of a type Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-30 02:53 +0000
    Re: unittest: assertRaises() with an instance instead of a type Terry Reedy <tjreedy@udel.edu> - 2012-03-28 14:26 -0400
      Re: unittest: assertRaises() with an instance instead of a type Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-03-29 09:28 +0200
        Re: unittest: assertRaises() with an instance instead of a type Terry Reedy <tjreedy@udel.edu> - 2012-03-29 11:04 -0400
      tabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type) Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-03-29 09:18 +0200
        Re: tabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type) Roy Smith <roy@panix.com> - 2012-03-29 08:49 -0400
        Re: tabs/spaces Dave Angel <d@davea.name> - 2012-03-29 11:16 -0400
        Re: tabs/spaces Terry Reedy <tjreedy@udel.edu> - 2012-03-29 11:25 -0400
          Re: tabs/spaces Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-03-30 09:05 +0200
            Re: tabs/spaces Dave Angel <d@davea.name> - 2012-03-30 08:47 -0400
              Re: tabs/spaces Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-04-02 09:12 +0200
                Re: tabs/spaces Terry Reedy <tjreedy@udel.edu> - 2012-04-02 03:42 -0400

Page 1 of 2  [1] 2  Next page →


#22275 — unittest: assertRaises() with an instance instead of a type

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-03-28 14:28 +0200
Subjectunittest: assertRaises() with an instance instead of a type
Message-ID<pncb49-ur7.ln1@satorlaser.homedns.org>
Hi!

I'm currently writing some tests for the error handling of some code. In 
this scenario, I must make sure that both the correct exception is 
raised and that the contained error code is correct:


   try:
       foo()
       self.fail('exception not raised')
   catch MyException as e:
       self.assertEqual(e.errorcode, SOME_FOO_ERROR)
   catch Exception:
       self.fail('unexpected exception raised')


This is tedious to write and read. The docs mention this alternative:


    with self.assertRaises(MyException) as cm:
        foo()
    self.assertEqual(cm.the_exception.errorcode, SOME_FOO_ERROR)


This is shorter, but I think there's an alternative syntax possible that 
would be even better:


     with self.assertRaises(MyException(SOME_FOO_ERROR)):
         foo()


Here, assertRaises() is not called with an exception type but with an 
exception instance. I'd implement it something like this:


     def assertRaises(self, exception, ...):
         # divide input parameter into type and instance
         if isinstance(exception, Exception):
             exception_type = type(exception)
         else:
             exception_type = exception
             exception = None
         # call testee and verify results
         try:
             ...call function here...
         except exception_type as e:
             if not exception is None:
                 self.assertEqual(e, exception)


This of course requires the exception to be equality-comparable.


Questions here:
1. Does this sound like a useful extension or am I missing another 
obvious solution to my problem?
2. The assertRaises() sketch above tries to auto-detect whether the 
given parameter is the type or an instance. Given the highly dynamic 
nature of Python, an object can be both instance and type, is the above 
detection mechanism reliable?


Of course I'm open for other suggestions to solve my problem. One that I 
thought of but which I haven't really looked into was to modify __init__ 
or __new__ of my exception class to return an instance of a derived 
class that uniquely identifies the error. I.e. 
MyException(SOME_FOO_ERROR) would not create a MyException instance but 
a MyFooErrorException instance (which has MyException as a baseclass). 
In that case, the existing code that checks for the right exception type 
would suffice for my needs.


Cheers everybody!

Uli

[toc] | [next] | [standalone]


#22289

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-28 18:07 +0000
Message-ID<4f735345$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#22275
On Wed, 28 Mar 2012 14:28:08 +0200, Ulrich Eckhardt wrote:

> Hi!
> 
> I'm currently writing some tests for the error handling of some code. In
> this scenario, I must make sure that both the correct exception is
> raised and that the contained error code is correct:
> 
> 
>    try:
>        foo()
>        self.fail('exception not raised')
>    catch MyException as e:
>        self.assertEqual(e.errorcode, SOME_FOO_ERROR)
>    catch Exception:
>        self.fail('unexpected exception raised')

First off, that is not Python code. "catch Exception" gives a syntax 
error.

Secondly, that is not the right way to do this unit test. You are testing 
two distinct things, so you should write it as two separate tests:


    def testFooRaisesException(self):
        # Test that foo() raises an exception.
        self.assertRaises(MyException, foo)


If foo does *not* raise an exception, the unittest framework will handle 
the failure for you. If it raises a different exception, the framework 
will also handle that too.

Then write a second test to check the exception code:

    def testFooExceptionCode(self):
        # Test that foo()'s exception has the right error code.
        try:
            foo()
        except MyException as err:
            self.assertEquals(err.errorcode, SOME_FOO_ERROR)
        

Again, let the framework handle any unexpected cases.

If you have lots of functions to test, write a helper function:

def catch(exception, func, *args, **kwargs):
    try:
        func(*args, **kwargs)
    except exception as err:
        return err
    raise RuntimeError('no error raised')


and then the test becomes:

    def testFooExceptionCode(self):
        # Test that foo()'s exception has the right error code.
        self.assertEquals(
            catch(MyException, foo).errorcode, SOME_FOO_ERROR
            )



(By the way, I have to question the design of an exception with error 
codes. That seems pretty poor design to me. Normally the exception *type* 
acts as equivalent to an error code.)


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#22322

FromBen Finney <ben+python@benfinney.id.au>
Date2012-03-29 12:55 +1100
Message-ID<87mx70f9xa.fsf@benfinney.id.au>
In reply to#22289
Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:

> (By the way, I have to question the design of an exception with error 
> codes. That seems pretty poor design to me. Normally the exception *type* 
> acts as equivalent to an error code.)

Have a look at Python's built-in OSError. The various errors from the
operating system can only be distinguished by the numeric code the OS
returns, so that's what to test on in one's unit tests.

-- 
 \              “In the long run, the utility of all non-Free software |
  `\      approaches zero. All non-Free software is a dead end.” —Mark |
_o__)                                                    Pilgrim, 2006 |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#22327

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-29 06:35 +0000
Message-ID<4f7402a8$0$29884$c3e8da3$5496439d@news.astraweb.com>
In reply to#22322
On Thu, 29 Mar 2012 12:55:13 +1100, Ben Finney wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:
> 
>> (By the way, I have to question the design of an exception with error
>> codes. That seems pretty poor design to me. Normally the exception
>> *type* acts as equivalent to an error code.)
> 
> Have a look at Python's built-in OSError. The various errors from the
> operating system can only be distinguished by the numeric code the OS
> returns, so that's what to test on in one's unit tests.

I'm familiar with OSError. It is necessary because OSError is a high-
level interface to low-level C errors. I wouldn't call it a good design 
though, I certainly wouldn't choose it if we were developing an error 
system from scratch and weren't constrained by compatibility with a more 
primitive error model (error codes instead of exceptions).

The new, revamped exception hierarchy in Python 3.3 will rationalise much 
(but not all) for this, unifying IOError and OSError and making error 
codes much less relevant:


http://www.python.org/dev/peps/pep-3151/



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#22329

FromPeter Otten <__peter__@web.de>
Date2012-03-29 08:55 +0200
Message-ID<mailman.1110.1333004116.3037.python-list@python.org>
In reply to#22322
Ben Finney wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:
> 
>> (By the way, I have to question the design of an exception with error
>> codes. That seems pretty poor design to me. Normally the exception *type*
>> acts as equivalent to an error code.)
> 
> Have a look at Python's built-in OSError. The various errors from the
> operating system can only be distinguished by the numeric code the OS
> returns, so that's what to test on in one's unit tests.
 
The core devs are working to fix that:

$ python3.2 -c'open("does-not-exist")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'does-not-exist'
$ python3.3 -c'open("does-not-exist")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'does-not-exist'

$ python3.2 -c'open("unwritable", "w")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IOError: [Errno 13] Permission denied: 'unwritable'
$ python3.3 -c'open("unwritable", "w")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
PermissionError: [Errno 13] Permission denied: 'unwritable'

http://www.python.org/dev/peps/pep-3151/

[toc] | [prev] | [next] | [standalone]


#22491

FromSteve Howell <showell30@yahoo.com>
Date2012-03-28 22:50 -0700
Message-ID<f6cbeb5e-bf88-4d89-8c08-7944c55b3068@oq7g2000pbb.googlegroups.com>
In reply to#22322
On Mar 28, 6:55 pm, Ben Finney <ben+pyt...@benfinney.id.au> wrote:
> Steven D'Aprano <steve+comp.lang.pyt...@pearwood.info> writes:
> > (By the way, I have to question the design of an exception with error
> > codes. That seems pretty poor design to me. Normally the exception *type*
> > acts as equivalent to an error code.)
>
> Have a look at Python's built-in OSError. The various errors from the
> operating system can only be distinguished by the numeric code the OS
> returns, so that's what to test on in one's unit tests.
>

To the extent that numeric error codes are poor design (see Steven's
comment) but part of the language (see Ben's comment), it may be
worthwhile to consider a pattern like below.

Let's say you have a function like save_config, where you know that
permissions might be an issue on some systems, but you don't want to
take any action (leave that to the callers).  In those cases, it might
be worthwhile to test for the specific error code (13), but then
translate it to a more domain-specific exception.  This way, all your
callers can trap for a much more specific exception than OSError.
Writing the test code for save_config still presents some of the
issues that the OP alluded to, but then other parts of the system can
be tested with simple assertRaises().


  import os

  class ConfigPermissionError:
    pass

  def save_config(config):
    try:
      dir = os.mkdir('/config')
    except OSError, e:
      if e[0] == 13:
        raise ConfigPermissionError()
      else:
        raise
    fn = os.path.join(dir, 'config.txt')
    f = open(fn, 'w')
    # and so on...

  try:
    save_config({'port': 500})
  except ConfigPermissionError:
    # do some workaround here
    print 'Config not saved due to permissions'

[toc] | [prev] | [next] | [standalone]


#22330

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-03-29 09:08 +0200
Message-ID<eced49-sge.ln1@satorlaser.homedns.org>
In reply to#22289
Am 28.03.2012 20:07, schrieb Steven D'Aprano:
> First off, that is not Python code. "catch Exception" gives a syntax
> error.

Old C++ habits... :|


> Secondly, that is not the right way to do this unit test. You are testing
> two distinct things, so you should write it as two separate tests:
[..code..]
> If foo does *not* raise an exception, the unittest framework will handle
> the failure for you. If it raises a different exception, the framework
> will also handle that too.
>
> Then write a second test to check the exception code:
[...]
> Again, let the framework handle any unexpected cases.

Sorry, you got it wrong, it should be three tests:
1. Make sure foo() raises an exception.
2. Make sure foo() raises the right exception.
3. Make sure the errorcode in the exception is right.

Or maybe you should in between verify that the exception raised actually 
contains an errorcode? And that the errorcode can be equality-compared 
to the expected value? :>

Sorry, I disagree that these steps should be separated. It would blow up 
the code required for testing, increasing maintenance burdens. Which 
leads back to a solution that uses a utility function, like the one you 
suggested or the one I was looking for initially.


> (By the way, I have to question the design of an exception with error
> codes. That seems pretty poor design to me. Normally the exception *type*
> acts as equivalent to an error code.)

True. Normally. I'd adapting to a legacy system though, similar to 
OSError, and that system simply emits error codes which the easiest way 
to handle is by wrapping them.


Cheers!

Uli

[toc] | [prev] | [next] | [standalone]


#22331

FromPeter Otten <__peter__@web.de>
Date2012-03-29 09:48 +0200
Message-ID<mailman.1111.1333007276.3037.python-list@python.org>
In reply to#22330
Ulrich Eckhardt wrote:

> True. Normally. I'd adapting to a legacy system though, similar to
> OSError, and that system simply emits error codes which the easiest way
> to handle is by wrapping them.

If you have

err = some_func()
if err: 
   raise MyException(err) 

the effort to convert it to

exc = lookup_exception(some_func())
if exc:
    raise exc

is small. A fancy way is to use a decorator:

#untested
def code_to_exception(table):
    def deco(f):
        def g(*args, **kw):
            err = f(*args, **kw)
            exc = table[err]
            if exc is not None: 
                raise exc
        return g
    return f

class MyError(Exception): pass
class HyperspaceBypassError(MyError): pass

@code_to_exception({42: HyperspaceBypassError, 0: None})
def some_func(...):
    # ...

[toc] | [prev] | [next] | [standalone]


#22372

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-30 02:45 +0000
Message-ID<4f751e31$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#22330
On Thu, 29 Mar 2012 09:08:30 +0200, Ulrich Eckhardt wrote:
> Am 28.03.2012 20:07, schrieb Steven D'Aprano:

>> Secondly, that is not the right way to do this unit test. You are
>> testing two distinct things, so you should write it as two separate
>> tests:
> [..code..]
>> If foo does *not* raise an exception, the unittest framework will
>> handle the failure for you. If it raises a different exception, the
>> framework will also handle that too.
>>
>> Then write a second test to check the exception code:
> [...]
>> Again, let the framework handle any unexpected cases.
> 
> Sorry, you got it wrong, it should be three tests: 1. Make sure foo()
> raises an exception. 2. Make sure foo() raises the right exception. 3.
> Make sure the errorcode in the exception is right.
> 
> Or maybe you should in between verify that the exception raised actually
> contains an errorcode? And that the errorcode can be equality-compared
> to the expected value? :>

Of course you are free to slice it even finer if you like:

testFooWillRaiseSomethingButIDontKnowWhat
testFooWillRaiseMyException
testFooWillRaiseMyExceptionWithErrorcode
testFooWillRaiseMyExceptionWithErrorcodeWhichSupportsEquality
testFooWillRaiseMyExceptionWithErrorcodeEqualToFooError

Five tests :)

To the degree that the decision of how finely to slice tests is a matter 
of personal judgement and/or taste, I was wrong to say "that is not the 
right way". I should have said "that is not how I would do that test".

I believe that a single test is too coarse, and three or more tests is 
too fine, but two tests is just right. Let me explain how I come to that 
judgement.

If you take a test-driven development approach, the right way to test 
this is to write testFooWillFail once you decide that foo() should raise 
MyException but before foo() actually does so. You would write the test, 
the test would fail, and you would fix foo() to ensure it raises the 
exception. Then you leave the now passing test in place to detect 
regressions.

Then you do the same for the errorcode. Hence two tests.

Since running tests is (usually) cheap, you never bother going back to 
remove tests which are made redundant by later tests. You only remove 
them if they are made redundant by chances to the code. So even though 
the first test is made redundant by the second (if the first fails, so 
will the second), you don't remove it.

Why not? Because it guards against regressions. Suppose I decide that 
errorcode is no longer needed, so I remove the test for errorcode. If I 
had earlier also removed the independent test for MyException being 
raised, I've now lost my only check against regressions in foo().

So: never remove tests just because they are redundant. Only remove them 
when they are obsolete due to changes in the code being tested.

Even when I don't actually write the tests in advance of the code, I 
still write them as if I were. That usually makes it easy for me to 
decide how fine grained the tests should be: since there was never a 
moment when I thought MyException should have an errorcode attribute, but 
not know what that attribute would be, I don't need a *separate* test for 
the existence of errorcode.

(I would only add such a separate test if there was a bug that sometimes 
the errorcode does not exist. That would be a regression test.)

The question of the exception type is a little more subtle. There *is* a 
moment when I knew that foo() should raise an exception, but before I 
decided what that exception would be. ValueError? TypeError? Something 
else? I can write the test before making that decision:

def testFooRaises(self):
    try:
        foo()
    except:  # catch anything
        pass
    else:
        self.fail("foo didn't raise")


However, the next step is broken: I have to modify foo() to raise an 
exception, and there is no "raise" equivalent to the bare "except", no 
way to raise an exception without specifying an exception type.

I can use a bare raise, but only in response to an existing exception. So 
to raise an exception at all, I need to decide what exception that will 
be. Even if I start with a placeholder "raise BaseException", and test 
for that, when I go back and change the code to "raise MyException" I 
should change the test, not create a new test.

Hence there is no point is testing for "any exception, I don't care what" 
since I can't write code corresponding to that test case. Hence, I end up 
with two tests, not three and certainly not five.




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#22389

FromEthan Furman <ethan@stoneleaf.us>
Date2012-03-30 10:45 -0700
Message-ID<mailman.1153.1333132489.3037.python-list@python.org>
In reply to#22372
Steven D'Aprano wrote:
> To the degree that the decision of how finely to slice tests is a matter 
> of personal judgement and/or taste, I was wrong to say "that is not the 
> right way". I should have said "that is not how I would do that test".
> 
> I believe that a single test is too coarse, and three or more tests is 
> too fine, but two tests is just right. Let me explain how I come to that 
> judgement.
> 
> If you take a test-driven development approach, the right way to test 
> this is to write testFooWillFail once you decide that foo() should raise 
> MyException but before foo() actually does so. You would write the test, 
> the test would fail, and you would fix foo() to ensure it raises the 
> exception. Then you leave the now passing test in place to detect 
> regressions.
> 
> Then you do the same for the errorcode. Hence two tests.

[snip]

> So: never remove tests just because they are redundant. Only remove them 
> when they are obsolete due to changes in the code being tested.

Very persuasive argument -- I now find myself disposed to writing two 
tests (not three, nor five ;).

~Ethan~

[toc] | [prev] | [next] | [standalone]


#22346

FromEthan Furman <ethan@stoneleaf.us>
Date2012-03-29 08:35 -0700
Message-ID<mailman.1121.1333036287.3037.python-list@python.org>
In reply to#22289
Steven D'Aprano wrote:
> On Wed, 28 Mar 2012 14:28:08 +0200, Ulrich Eckhardt wrote:
> 
>> Hi!
>>
>> I'm currently writing some tests for the error handling of some code. In
>> this scenario, I must make sure that both the correct exception is
>> raised and that the contained error code is correct:
>>
>>
>>    try:
>>        foo()
>>        self.fail('exception not raised')
>>    catch MyException as e:
>>        self.assertEqual(e.errorcode, SOME_FOO_ERROR)
>>    catch Exception:
>>        self.fail('unexpected exception raised')
> 
> Secondly, that is not the right way to do this unit test. You are testing 
> two distinct things, so you should write it as two separate tests:

I have to disagree -- I do not see the advantage of writing a second 
test that *will* fail if the first test fails as opposed to bundling 
both tests together, and having one failure.

~Ethan~

[toc] | [prev] | [next] | [standalone]


#22373

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-30 02:53 +0000
Message-ID<4f75203a$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#22346
On Thu, 29 Mar 2012 08:35:16 -0700, Ethan Furman wrote:

> Steven D'Aprano wrote:
>> On Wed, 28 Mar 2012 14:28:08 +0200, Ulrich Eckhardt wrote:
>> 
>>> Hi!
>>>
>>> I'm currently writing some tests for the error handling of some code.
>>> In this scenario, I must make sure that both the correct exception is
>>> raised and that the contained error code is correct:
>>>
>>>
>>>    try:
>>>        foo()
>>>        self.fail('exception not raised')
>>>    catch MyException as e:
>>>        self.assertEqual(e.errorcode, SOME_FOO_ERROR)
>>>    catch Exception:
>>>        self.fail('unexpected exception raised')
>> 
>> Secondly, that is not the right way to do this unit test. You are
>> testing two distinct things, so you should write it as two separate
>> tests:
> 
> I have to disagree -- I do not see the advantage of writing a second
> test that *will* fail if the first test fails as opposed to bundling
> both tests together, and having one failure.

Using that reasoning, your test suite should contain *one* ginormous test 
containing everything:

def testDoesMyApplicationWorkPerfectly(self):
    # TEST ALL THE THINGS!!!
    ...


since *any* failure in any part will cause cascading failures in every 
other part of the software which relies on that part. If you have a tree 
of dependencies, a failure in the root of the tree will cause everything 
to fail, and so by your reasoning, everything should be in a single test.

I do not agree with that reasoning, even when the tree consists of two 
items: an exception and an exception attribute.

The problem of cascading test failures is a real one. But I don't believe 
that the solution is to combine multiple conceptual tests into a single 
test. In this case, the code being tested covers two different concepts:

1. foo() will raise MyException. Hence one test for this.

2. When foo() raises MyException, the exception instance will include an
   errorcode attribute with a certain value. This is conceptually 
   separate from #1 above, even though it depends on it. 

Why is it conceptually separate? Because there may be cases where the 
caller cares about foo() raising MyException, but doesn't care about the 
errorcode. Hence errorcode is dependent but separate, and hence a 
separate test.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#22297

FromTerry Reedy <tjreedy@udel.edu>
Date2012-03-28 14:26 -0400
Message-ID<mailman.1088.1332959227.3037.python-list@python.org>
In reply to#22275
On 3/28/2012 8:28 AM, Ulrich Eckhardt wrote:
> Hi!
>
> I'm currently writing some tests for the error handling of some code. In
> this scenario, I must make sure that both the correct exception is
> raised and that the contained error code is correct:
>
>
> try:
> foo()
> self.fail('exception not raised')
> catch MyException as e:
> self.assertEqual(e.errorcode, SOME_FOO_ERROR)
> catch Exception:
> self.fail('unexpected exception raised')
>
>
> This is tedious to write and read. The docs mention this alternative:
>
>
> with self.assertRaises(MyException) as cm:
> foo()
> self.assertEqual(cm.the_exception.errorcode, SOME_FOO_ERROR)

Exceptions can have multiple attributes. This allows the tester to 
exactly specify what attributes to test.

> This is shorter, but I think there's an alternative syntax possible that
> would be even better:
>
> with self.assertRaises(MyException(SOME_FOO_ERROR)):
> foo()

I presume that if this worked the way you want, all attributes would 
have to match. The message part of builtin exceptions is allowed to 
change, so hard-coding an exact expected message makes tests fragile. 
This is a problem with doctest.

> Here, assertRaises() is not called with an exception type but with an
> exception instance. I'd implement it something like this:
>
> def assertRaises(self, exception, ...):
> # divide input parameter into type and instance
> if isinstance(exception, Exception):
> exception_type = type(exception)
> else:
> exception_type = exception
> exception = None
> # call testee and verify results
> try:
> ...call function here...
> except exception_type as e:
> if not exception is None:
> self.assertEqual(e, exception)

Did you use tabs? They do not get preserved indefinitely, so they are 
bad for posting.

> This of course requires the exception to be equality-comparable.

Equality comparison is by id. So this code will not do what you want.

You can, of course, write a custom AssertX subclass that at least works 
for your custom exception class.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#22332

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-03-29 09:28 +0200
Message-ID<ghfd49-qje.ln1@satorlaser.homedns.org>
In reply to#22297
Am 28.03.2012 20:26, schrieb Terry Reedy:
> On 3/28/2012 8:28 AM, Ulrich Eckhardt wrote:
>> with self.assertRaises(MyException(SOME_FOO_ERROR)):
>>     foo()
>
> I presume that if this worked the way you want, all attributes would
> have to match. The message part of builtin exceptions is allowed to
> change, so hard-coding an exact expected message makes tests fragile.
> This is a problem with doctest.

I would have assumed that comparing two exceptions leaves out messages 
that are intended for the user, not as part of the API. However, my 
expectations aren't met anyway, because ...

>> This of course requires the exception to be equality-comparable.
>
> Equality comparison is by id. So this code will not do what you want.

  >>> Exception('foo') == Exception('foo')
  False

Yikes! That was unexpected and completely changes my idea. Any clue 
whether this is intentional? Is identity the fallback when no equality 
is defined for two objects?

Thanks for your feedback!

Uli

[toc] | [prev] | [next] | [standalone]


#22341

FromTerry Reedy <tjreedy@udel.edu>
Date2012-03-29 11:04 -0400
Message-ID<mailman.1117.1333033510.3037.python-list@python.org>
In reply to#22332
On 3/29/2012 3:28 AM, Ulrich Eckhardt wrote:

>> Equality comparison is by id. So this code will not do what you want.
>
>  >>> Exception('foo') == Exception('foo')
> False
>
> Yikes! That was unexpected and completely changes my idea. Any clue
> whether this is intentional? Is identity the fallback when no equality
> is defined for two objects?

Yes. The Library Reference 4.3. Comparisons (for built-in classes) puts 
is this way.
"Objects of different types, except different numeric types, never 
compare equal. Furthermore, some types (for example, function objects) 
support only a degenerate notion of comparison where any two objects of 
that type are unequal." In other words, 'a==b' is the same as 'a is b'. 
That is also the default for user-defined classes, but I am not sure 
where that is documented, if at all.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#22333 — tabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type)

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-03-29 09:18 +0200
Subjecttabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type)
Message-ID<0ved49-hie.ln1@satorlaser.homedns.org>
In reply to#22297
Am 28.03.2012 20:26, schrieb Terry Reedy:
> On 3/28/2012 8:28 AM, Ulrich Eckhardt wrote:
[...]
>> # call testee and verify results
>> try:
>> ...call function here...
>> except exception_type as e:
>> if not exception is None:
>> self.assertEqual(e, exception)
>
> Did you use tabs? They do not get preserved indefinitely, so they are
> bad for posting.

I didn't consciously use tabs, actually I would rather avoid them. That 
said, my posting looks correctly indented in my "sent" folder and also 
in the copy received from my newsserver. What could also have an 
influence is line endings. I'm using Thunderbird on win32 here, acting 
as news client to comp.lang.python. Or maybe it's your software (or 
maybe some software in between) that fails to preserve formatting.

*shrug*

Uli

[toc] | [prev] | [next] | [standalone]


#22336 — Re: tabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type)

FromRoy Smith <roy@panix.com>
Date2012-03-29 08:49 -0400
SubjectRe: tabs/spaces (was: Re: unittest: assertRaises() with an instance instead of a type)
Message-ID<roy-326BF0.08495929032012@news.panix.com>
In reply to#22333
In article <0ved49-hie.ln1@satorlaser.homedns.org>,
 Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> wrote:

> I didn't consciously use tabs, actually I would rather avoid them. That 
> said, my posting looks correctly indented in my "sent" folder and also 
> in the copy received from my newsserver. What could also have an 
> influence is line endings. I'm using Thunderbird on win32 here, acting 
> as news client to comp.lang.python. Or maybe it's your software (or 
> maybe some software in between) that fails to preserve formatting.
> 
> *shrug*

Oh noes!  The line eater bug is back!

[toc] | [prev] | [next] | [standalone]


#22343 — Re: tabs/spaces

FromDave Angel <d@davea.name>
Date2012-03-29 11:16 -0400
SubjectRe: tabs/spaces
Message-ID<mailman.1119.1333034206.3037.python-list@python.org>
In reply to#22333
On 03/29/2012 03:18 AM, Ulrich Eckhardt wrote:
> Am 28.03.2012 20:26, schrieb Terry Reedy:
>> On 3/28/2012 8:28 AM, Ulrich Eckhardt wrote:
> [...]
>>> # call testee and verify results
>>> try:
>>> ...call function here...
>>> except exception_type as e:
>>> if not exception is None:
>>> self.assertEqual(e, exception)
>>
>> Did you use tabs? They do not get preserved indefinitely, so they are
>> bad for posting.
>
> I didn't consciously use tabs, actually I would rather avoid them. 
> That said, my posting looks correctly indented in my "sent" folder and 
> also in the copy received from my newsserver. What could also have an 
> influence is line endings. I'm using Thunderbird on win32 here, acting 
> as news client to comp.lang.python. Or maybe it's your software (or 
> maybe some software in between) that fails to preserve formatting.
>
> *shrug*
>
> Uli

More likely, you failed to tell Thunderbird to send it as text.  Html 
messages will read differently on html aware readers than on the 
standard text readers.  They also take maybe triple the space and bandwidth.

In thunderbird  3.1.19
In Edit->Preferences, Composition->general  Configure Text Format 
Behavior -> SendOptions  In that dialog, under Text Format, choose 
Convert the message to plain text.  Then in the tab called "Plain text 
domains", add  python.org




-- 

DaveA

[toc] | [prev] | [next] | [standalone]


#22344 — Re: tabs/spaces

FromTerry Reedy <tjreedy@udel.edu>
Date2012-03-29 11:25 -0400
SubjectRe: tabs/spaces
Message-ID<mailman.1120.1333034733.3037.python-list@python.org>
In reply to#22333
On 3/29/2012 3:18 AM, Ulrich Eckhardt wrote:
> Am 28.03.2012 20:26, schrieb Terry Reedy:
>> On 3/28/2012 8:28 AM, Ulrich Eckhardt wrote:
> [...]
>>> # call testee and verify results
>>> try:
>>> ...call function here...
>>> except exception_type as e:
>>> if not exception is None:
>>> self.assertEqual(e, exception)
>>
>> Did you use tabs? They do not get preserved indefinitely, so they are
>> bad for posting.
>
> I didn't consciously use tabs, actually I would rather avoid them. That
> said, my posting looks correctly indented in my "sent" folder and also
> in the copy received from my newsserver. What could also have an
> influence is line endings. I'm using Thunderbird on win32 here, acting
> as news client to comp.lang.python.

I am using Thunderbird, win64, as news client for gmane. The post looked 
fine as originally received. The indents only disappeared when I hit 
reply and the >s were added. That does not happen, in general, for other 
messages. Unfortunately I cannot go back and read that message as 
received because the new version of Tbird is misbehaving and deleting 
read messages on close even though I asked to keep them 6 months. I will 
look immediately when I next see indents disappearing.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#22377 — Re: tabs/spaces

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-03-30 09:05 +0200
SubjectRe: tabs/spaces
Message-ID<hj2g49-nsk.ln1@satorlaser.homedns.org>
In reply to#22344
Am 29.03.2012 17:25, schrieb Terry Reedy:
> I am using Thunderbird, win64, as news client for gmane. The post looked
> fine as originally received. The indents only disappeared when I hit
> reply and the >s were added.

I can confirm this misbehaviour of Thunderbird (version 11.0 here), it 
strips the leading spaces when you begin a reply.

Uli

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.python


csiph-web