Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #22275 > unrolled thread
| Started by | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| First post | 2012-03-28 14:28 +0200 |
| Last post | 2012-04-02 03:42 -0400 |
| Articles | 20 on this page of 23 — 9 participants |
Back to article view | Back to comp.lang.python
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 →
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-03-28 14:28 +0200 |
| Subject | unittest: 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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-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]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2012-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-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]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2012-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]
| From | Steve Howell <showell30@yahoo.com> |
|---|---|
| Date | 2012-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]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-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]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2012-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-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]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2012-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]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2012-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-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]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2012-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]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-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]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2012-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]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-03-29 09:18 +0200 |
| Subject | tabs/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]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2012-03-29 08:49 -0400 |
| Subject | Re: 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]
| From | Dave Angel <d@davea.name> |
|---|---|
| Date | 2012-03-29 11:16 -0400 |
| Subject | Re: 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]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2012-03-29 11:25 -0400 |
| Subject | Re: 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]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-03-30 09:05 +0200 |
| Subject | Re: 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