Hi all
I asked students of my lecture to write SUnit tests for some collections and I noticed that in Squeak the collection library do not use specific exception but instead use error:. The consequence is that it can be tedious to capture only specific error. I was wondering what is the general feeling about this state. In VW they have various exception that can be trapped more specifically. Stef |
Hi Stef,
let's have a look a the following example: Intervall >> remove: anElement self error: 'elements cannot be removed from an Interval' What do you mean with "trapped more specifically"? I would claim that almost all (happy for any counter example) uses of "self error:" are indicating that some (maybe implicit) precondition fails. Being radical I would say in above situation the precondition just fails always, meaning that this method would never make sense to be called on Intervals. Using traits we shouldn't need the strange idiom to overwrite template or other superclass methods, that do not work in special subclasses, any more. (It would be really tedious to enumerate all methods on all classes which do not work... ;-) If the error string is telling me why the precondition fails, good. Why would I want to bother with different kinds of exceptions here? By the way, I call a test, which tests the failing of a precondition, "pessimistic method example", but I am still open for a better name. People did not like the name "counter example" or "negative example", though I still would prefer one of these names and I am about to rename... Cheers, Markus On Mar 1, 2006, at 10:29 PM, stéphane ducasse wrote: > Hi all > > I asked students of my lecture to write SUnit tests for some > collections and I noticed that in Squeak > the collection library do not use specific exception but instead > use error:. > The consequence is that it can be tedious to capture only specific > error. I was wondering what is the > general feeling about this state. In VW they have various exception > that can be trapped more specifically. > > Stef > |
In reply to this post by stéphane ducasse-2
stéphane ducasse <[hidden email]> wrote: > Hi all > > I asked students of my lecture to write SUnit tests for some > collections and I noticed that in Squeak > the collection library do not use specific exception but instead use > error:. In a Squeak3.8-5989 basic image we have 850 senders of error: > The consequence is that it can be tedious to capture only specific > error. Yes, it is often necessary to ask an exception for its message text to understand what happened. Checking the message texts is of course a bit risky, they can be changed at any time, they may be localized and so on. > I was wondering what is the > general feeling about this state. In VW they have various exception > that can be trapped more specifically. > The collection relevant senders of error: are coded in these methods: Object>>errorSubscriptBounds: Object>>errorNonIntegerIndex Collection>>errorEmptyCollection Collection>>errorNotKeyed Dictionary>>errorKeyNotFound Dictionary>>errorValueNotFound SequenceableCollection>>checkedAt: SequenceableCollection>>errorFirstObject: SequenceableCollection>>errorLastObject: SequenceableCollection>>errorOutOfBounds Set>>add: SequenceableCollection>>with:do: SequenceableCollection>>with:collect: Using specialized exceptions is attractive, but it is not easy to find a good exception hierarchy. What do you think about this hierarchy for collection related errors? Error CollectionAccessError IndexOutOfBoundsError KeyNotFoundError EmptyCollectionError This would give you the possibility to write handler like: [collection at: item] on: CollectionAccessError do: [:exception | " exception class tells you more about the nature of the error " ]. Apart form the problem of finding a good hierarchy, we have to ask ourselves how many exception classes we want to have. For teaching purposes, it is a good idea to implement a stack with a FullStackError and an EmptyStackError and a common superclass StackError, but is it really necessary to have tenths or hundreds of specialized error classes in a general purpose image? I think I would like to have specialized exceptions for collections, but perhaps this is something one should try to find out whether there are hidden disadvantages. Greetings, Boris |
In reply to this post by Markus Gälli-3
On 2 mars 06, at 11:12, Markus Gaelli wrote: > Hi Stef, > > let's have a look a the following example: > > Intervall >> remove: anElement > self error: 'elements cannot be removed from an Interval' > > What do you mean with "trapped more specifically"? Not for this one but IndexNotFoundError KeyNotFoundError SubscriptOutOfBoundsError > I would claim that almost all (happy for any counter example) uses > of "self error:" are indicating that some (maybe implicit) > precondition fails. > Being radical I would say in above situation the precondition just > fails always, meaning that this method would never make sense to be > called on Intervals. But I'm not talking about that in particular. Dictionary>>at: key "Answer the value associated with the key." ^ self at: key ifAbsent: [self errorKeyNotFound] errorKeyNotFound self error: 'key not found' errorValueNotFound self error: 'value not found' > Using traits we shouldn't need the strange idiom to overwrite > template or other superclass methods, that do not work in special > subclasses, any more. > (It would be really tedious to enumerate all methods on all classes > which do not work... ;-) Sure but this was not my point. > If the error string is telling me why the precondition fails, good. > Why would I want to bother with different kinds of exceptions here? > > By the way, I call a test, which tests the failing of a > precondition, "pessimistic method example", but I am still open for > a better name. > People did not like the name "counter example" or "negative > example", though I still would prefer one of these names and I am > about to rename... > > Cheers, > > Markus > > On Mar 1, 2006, at 10:29 PM, stéphane ducasse wrote: > >> Hi all >> >> I asked students of my lecture to write SUnit tests for some >> collections and I noticed that in Squeak >> the collection library do not use specific exception but instead >> use error:. >> The consequence is that it can be tedious to capture only specific >> error. I was wondering what is the >> general feeling about this state. In VW they have various >> exception that can be trapped more specifically. >> >> Stef >> > > > |
In reply to this post by Boris.Gaertner
Hi Boris,
>> The consequence is that it can be tedious to capture only specific >> error. > Yes, it is often necessary to ask an exception for its message > text to understand what happened. Could you give an example please? I am still not convinced that the idiom of "first (possibly) hitting the wall, and then asking for the door" is a good one to teach students programming. I think it would be better to teach students a _defensive_ way of programming, that is to ask first, if everything is ok, using some boolean queries and if these queries do not exist, let them write these queries and _not_ exception hierarchies. Exceptions are a way of goto programming and can become quite hairy to use. I am glad to be in sync here with Andrew Thomas and Dave Hunt in their nice book about the pragmatic programmer. They write something like using exceptions should be actually saved for situations, the developer cannot predict, such as external io- failures etc. I am all for letting the programmers know which precondition they violated calling a method, but only for debugging their code, and not for using that info _in_ their code. I am aware that there is a slight performance penalty to ask if everything is ok first all the times, also when everything _is_ ok. But I'd teach above idiom more as the exception than as the rule... ;-) -- if at all. Cheers, Markus |
In reply to this post by Boris.Gaertner
>> The consequence is that it can be tedious to capture only specific
>> error. > Yes, it is often necessary to ask an exception for its message > text to understand what happened. Checking the message texts > is of course a bit risky, they can be changed at any time, > they may be localized and so on. Indeed > >> I was wondering what is the >> general feeling about this state. In VW they have various exception >> that can be trapped more specifically. >> > > The collection relevant senders of error: are > coded in these methods: > > Object>>errorSubscriptBounds: > Object>>errorNonIntegerIndex > > Collection>>errorEmptyCollection > Collection>>errorNotKeyed > Dictionary>>errorKeyNotFound > Dictionary>>errorValueNotFound > SequenceableCollection>>checkedAt: > SequenceableCollection>>errorFirstObject: > SequenceableCollection>>errorLastObject: > SequenceableCollection>>errorOutOfBounds > Set>>add: > > SequenceableCollection>>with:do: > SequenceableCollection>>with:collect: > > Using specialized exceptions is attractive, but > it is not easy to find a good exception > hierarchy. What do you think about this > hierarchy for collection related errors? > > Error > CollectionAccessError > IndexOutOfBoundsError > KeyNotFoundError > EmptyCollectionError > > This would give you the possibility to write > handler like: > > [collection at: item] > on: CollectionAccessError > do: > [:exception | > " exception class tells you more about > the nature of the error " > ]. > > > Apart form the problem of finding a good > hierarchy, Which is not easy > we have to ask ourselves how many > exception classes we want to have. For > teaching purposes, it is a good idea to > implement a stack with a FullStackError > and an EmptyStackError and a common > superclass StackError, but is it really > necessary to have tenths or hundreds of > specialized error classes in a general purpose > image? Exactly. I would be against it. I think that exception are often a Java plague :) Still I was thinking that thinking about that could be interesting. > I think I would like to have specialized exceptions > for collections, but perhaps this is something one > should try to find out whether there are hidden > disadvantages. Thanks for the email because it shows that we are in sync. Stef > > Greetings, > Boris > |
In reply to this post by Markus Gälli-3
On 2 mars 06, at 13:49, Markus Gaelli wrote: > Hi Boris, > >>> The consequence is that it can be tedious to capture only specific >>> error. >> Yes, it is often necessary to ask an exception for its message >> text to understand what happened. > > Could you give an example please? > > I am still not convinced that the idiom of "first (possibly) > hitting the wall, and then asking for the door" is a good one to > teach students programming. I was not talking about teaching anything. I just asked them to write tests and I was surprised that I have to catch always Error and not more precise Exception. > I think it would be better to teach students a _defensive_ way of > programming, that is to ask first, if everything is ok, using some > boolean queries and if these queries do not exist, let them write > these queries and _not_ exception hierarchies. > > Exceptions are a way of goto programming and can become quite hairy > to use. I am glad to be in sync here with Andrew Thomas and Dave > Hunt in their nice book about the pragmatic programmer. > They write something like using exceptions should be actually saved > for situations, the developer cannot predict, such as external io- > failures etc. Still in Squeak you have error in collections and read the email of Boris this is interesting to ask ourselves the question. > I am all for letting the programmers know which precondition they > violated calling a method, but only for debugging their code, and > not for using that info _in_ their code. I do not like exception either (but catching too high exception forces you to typecheck them anyway to do something). > > I am aware that there is a slight performance penalty to ask if > everything is ok first all the times, also when everything _is_ ok. > But I'd teach above idiom more as the exception than as the > rule... ;-) -- if at all. > > Cheers, > > Markus > > |
In reply to this post by stéphane ducasse-2
Hi Stef,
>> let's have a look a the following example: >> >> Intervall >> remove: anElement >> self error: 'elements cannot be removed from an Interval' >> >> What do you mean with "trapped more specifically"? > > Not for this one > but > IndexNotFoundError > KeyNotFoundError > SubscriptOutOfBoundsError > >> I would claim that almost all (happy for any counter example) uses >> of "self error:" are indicating that some (maybe implicit) >> precondition fails. >> Being radical I would say in above situation the precondition just >> fails always, meaning that this method would never make sense to >> be called on Intervals. > > But I'm not talking about that in particular. > > Dictionary>>at: key > "Answer the value associated with the key." > > ^ self at: key ifAbsent: [self errorKeyNotFound] > > errorKeyNotFound > > self error: 'key not found' > > errorValueNotFound > > self error: 'value not found' > as I answered Boris: I am still not convinced that the idiom of "first (possibly) hitting the wall, and then asking for the door" is a good one to teach students programming. I think it would be better to teach students a _defensive_ way of programming, that is to ask first, if everything is ok, using some boolean queries and if these queries do not exist, let them write these queries and _not_ exception hierarchies. So in your examples I would teach to use Dictionary >> includesKey: Dictionary >> includes: _before_ calling Dictionary >> atKey: Dictionary >> keyAtValue: respectively. Alternatively Dictionary >> at: key ifAbsent: exceptionBlock keyAtValue: value ifAbsent: exceptionBlock are certainly often our best friends here - without having the need for any "real" exceptions (and also too much asking before...) ;-) Cheers, Markus |
In reply to this post by stéphane ducasse-2
>> >> Could you give an example please? >> >> I am still not convinced that the idiom of "first (possibly) >> hitting the wall, and then asking for the door" is a good one to >> teach students programming. > > I was not talking about teaching anything. Ok, I had some nice discussions about that subject in basically all my Java classes I had to give... guess I was mixing that up a bit. ;-) Students love to throw and catch exceptions. For whom it might concern, there is > I just asked them to write tests and I was surprised that I have to > catch always Error and not more precise Exception. > > >> I think it would be better to teach students a _defensive_ way of >> programming, that is to ask first, if everything is ok, using some >> boolean queries and if these queries do not exist, let them write >> these queries and _not_ exception hierarchies. >> >> Exceptions are a way of goto programming and can become quite >> hairy to use. I am glad to be in sync here with Andrew Thomas and >> Dave Hunt in their nice book about the pragmatic programmer. >> They write something like using exceptions should be actually >> saved for situations, the developer cannot predict, such as >> external io-failures etc. > > Still in Squeak you have error in collections and read the email of > Boris this is interesting to ask ourselves the question. > >> I am all for letting the programmers know which precondition they >> violated calling a method, but only for debugging their code, and >> not for using that info _in_ their code. > > I do not like exception either (but catching too high exception > forces you to typecheck them anyway to do something). much trouble. The only three types of exception handling code, that come to my mind now, and which I understand, are: - External IO access, which might fail during the call - Unit-Tests to check if preconditions are _failing_ correctly - The _one_ most outer exception-handling code in a deployed application, which translates an ugly walkback/red screen/whatever into a nice message to the user to calm down, everything will be fixed, and to call that number of the helpdesk... Any I forgot? Btw., Java folks (in this case Bruce-"Thinking in C++/Java"-Eckel) came up with funny ways _not_ to be forced to have exception handling code: http://www.mindview.net/Etc/Discussions/CheckedExceptions Cheers, Markus >> >> I am aware that there is a slight performance penalty to ask if >> everything is ok first all the times, also when everything _is_ ok. >> But I'd teach above idiom more as the exception than as the >> rule... ;-) -- if at all. >> >> Cheers, >> >> Markus >> >> > > |
In reply to this post by stéphane ducasse-2
It would be easier to catch and therefore to test with a specific subclass of Exception. Besides, for the Interval>>remove: example, the code would have been: Interval>> remove: anElement "elements cannot be removed from an Interval" ^self shouldNotImplement Mind you, even in VW you have these calls of error:, and shouldNotImplement is implemented that way... But why not implementing shouldNotImplement like: Object>> shouldNotImplement ^ShouldNotImplementError raise Markus Gaelli wrote: Hi Stef,
To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. |
Hi Pascal,
> It would be easier to catch and therefore to test with a specific > subclass of Exception. What is wrong with writing: IntervalTest >> testRemove self should: [(1 to: 3) remove: 2] raise: Exception > Besides, for the Interval>>remove: example, the code would have been: > > Interval>> remove: anElement > "elements cannot be removed from an Interval" > ^self shouldNotImplement So I need to browse the method and cannot see directly anymore what is going in the label of my walkback-window? > > Mind you, even in VW you have these calls of error:, and > shouldNotImplement is implemented that way... > But why not implementing shouldNotImplement like: > > Object>> shouldNotImplement > ^ShouldNotImplementError raise Why? Cheers, Markus > > > > Markus Gaelli wrote: > Hi Stef, > > let's have a look a the following example: > > Intervall >> remove: anElement > self error: 'elements cannot be removed from an Interval' > > What do you mean with "trapped more specifically"? > > I would claim that almost all (happy for any counter example) uses of > "self error:" are indicating that some (maybe implicit) precondition > fails. > Being radical I would say in above situation the precondition just > fails always, meaning that this method would never make sense to be > called on Intervals. > > Using traits we shouldn't need the strange idiom to overwrite > template or other superclass methods, that do not work in special > subclasses, any more. > (It would be really tedious to enumerate all methods on all classes > which do not work... ;-) > > If the error string is telling me why the precondition fails, good. > Why would I want to bother with different kinds of exceptions here? > > By the way, I call a test, which tests the failing of a precondition, > "pessimistic method example", but I am still open for a better name. > People did not like the name "counter example" or "negative example", > though I still would prefer one of these names and I am about to > rename... > > Cheers, > > Markus > > On Mar 1, 2006, at 10:29 PM, stéphane ducasse wrote: > > > Hi all > > > > I asked students of my lecture to write SUnit tests for some > > collections and I noticed that in Squeak > > the collection library do not use specific exception but instead > > use error:. > > The consequence is that it can be tedious to capture only specific > > error. I was wondering what is the > > general feeling about this state. In VW they have various exception > > that can be trapped more specifically. > > > > Stef > > > > > To help you stay safe and secure online, we've developed the all > new Yahoo! Security Centre. > |
Markus Gaelli wrote: > Hi Pascal, > >> It would be easier to catch and therefore to test with a specific >> subclass of Exception. > > > What is wrong with writing: > > IntervalTest >> testRemove > self should: [(1 to: 3) remove: 2] raise: Exception Presumably because the class you are testing may be calling another class to do some work and *it* might be raising an exception. It's a little hard to imagine a good example for this particular case but perhaps it's looking up a preference and Squeak can't find the Preferences file. But if that preferences file *was* there, then that could would happily carry on without raising an exception. Your test *should* be failing, it's just getting disguised. But I do agree there is some difference between, say, a network exception that might prompt you to retry your behaviour vs. an exception saying "you're using the code wrong, buddy!". In the latter case, there does seem to be less advantage to having specific subclasses. We should keep in mind that subclassing exceptions is not just to provide the ability to differentiate between them when they are caught though. One of the great powers of exceptions here is having the ability to specify default behaviour. This allows the programmer to catch all exceptions during debugging but may in fact allow the program (or the user) to decide to continue on with a valid default when the program is really running. Sorry for the stream of consciousness email... :) Julian |
In reply to this post by Markus Gälli-3
> What is wrong with writing:
> > IntervalTest >> testRemove > self should: [(1 to: 3) remove: 2] raise: Exception If you accidently write (note the typing error in the selector) IntervalTest >> testRemove self should: [(1 to: 3) remve: 2] raise: Exception you get a test that always succeeds. Lukas -- Lukas Renggli http://www.lukas-renggli.ch |
Pascal, Lukas and Julian,
>> What is wrong with writing: >> >> IntervalTest >> testRemove >> self should: [(1 to: 3) remove: 2] raise: Exception > > If you accidently write (note the typing error in the selector) > > IntervalTest >> testRemove > self should: [(1 to: 3) remve: 2] raise: Exception > > you get a test that always succeeds. > and > Presumably because the class you are testing may be calling another > class to do some work and *it* might be raising an exception. It's > a little hard to imagine a good example for this particular case > but perhaps it's looking up a preference and Squeak can't find the > Preferences file. But if that preferences file *was* there, then > that could would happily carry on without raising an exception. > Your test *should* be failing, it's just getting disguised. > > But I do agree there is some difference between, say, a network > exception that might prompt you to retry your behaviour vs. an > exception saying "you're using the code wrong, buddy!". In the > latter case, there does seem to be less advantage to having > specific subclasses. > > We should keep in mind that subclassing exceptions is not just to > provide the ability to differentiate between them when they are > caught though. One of the great powers of exceptions here is > having the ability to specify default behaviour. This allows the > programmer to catch all exceptions during debugging but may in fact > allow the program (or the user) to decide to continue on with a > valid default when the program is really running. > > Sorry for the stream of consciousness email... :) Thanks for clarifying. These counter examples make much sense. So I'd suggest to come up with sth like: PreconditionError >> error: aString so that above became: IntervalTest >> testRemove self should: [(1 to: 3) remove: 2] raise: PreconditionError What do you think? Could you agree to see this call as violating a precondition? Think of the receiver as the 0th parameter, in that case certainly always wrong...aeh...is it? Actually creating an empty interval seems to be allowed. So why can't I remove an element of an interval with only one value? Speaking more general, removing the last or first element of an interval would keep the class invariant intact - if the interval has at least one element of course. Cheers, Markus |
In reply to this post by stéphane ducasse-2
stéphane ducasse wrote:
>> Intervall >> remove: anElement >> self error: 'elements cannot be removed from an Interval' >> >> What do you mean with "trapped more specifically"? > > Not for this one > but > IndexNotFoundError > KeyNotFoundError > SubscriptOutOfBoundsError I have often wondered about the usefulness of such hierarchies. The way I look at it, an exception hierarchy is useful if and only if one has a need to handle the exceptions individually. And what's interesting here is that in particular the above seem to be cases where nobody ever handles them (and doesn't have the need to since there are always variants that explicitly avoid the error condition like #at:ifAbsent: #removeKey:ifAbsent: etc). Therefore I'd claim that if you have a case where a client handles an exception by looking at the error text, then by all means, make it a separate entity. But otherwise, don't make the system more complex than it needs to be. Cheers, - Andreas |
In reply to this post by Markus Gälli-3
Markus
can you read what I wrote.... I'm not teaching anything here related to first checking if a key exist or not, or how to use a dictionary...... I'm teaching them to write tests and to write tests for a class that raises exceptions you have to catch them and to cover the potential behavior of the class! This is not my wish or not to have exceptions, they are there and to cover the method behavior (because of different programming style) I have to cover them. For example, what happens if we access an element at not existing index. Stef On 2 mars 06, at 14:14, Markus Gaelli wrote: > Hi Stef, > >>> let's have a look a the following example: >>> >>> Intervall >> remove: anElement >>> self error: 'elements cannot be removed from an Interval' >>> >>> What do you mean with "trapped more specifically"? >> >> Not for this one >> but >> IndexNotFoundError >> KeyNotFoundError >> SubscriptOutOfBoundsError >> >>> I would claim that almost all (happy for any counter example) >>> uses of "self error:" are indicating that some (maybe implicit) >>> precondition fails. >>> Being radical I would say in above situation the precondition >>> just fails always, meaning that this method would never make >>> sense to be called on Intervals. >> >> But I'm not talking about that in particular. >> >> Dictionary>>at: key >> "Answer the value associated with the key." >> >> ^ self at: key ifAbsent: [self errorKeyNotFound] >> >> errorKeyNotFound >> >> self error: 'key not found' >> >> errorValueNotFound >> >> self error: 'value not found' >> > > as I answered Boris: > I am still not convinced that the idiom of "first (possibly) > hitting the wall, and then asking for the door" is a good one to > teach students programming. > I think it would be better to teach students a _defensive_ way of > programming, that is to ask first, if everything is ok, using some > boolean queries and if these queries do not exist, let them write > these queries and _not_ exception hierarchies. > > So in your examples I would teach to use > Dictionary >> includesKey: > Dictionary >> includes: > _before_ calling > Dictionary >> atKey: > Dictionary >> keyAtValue: > respectively. > > Alternatively > Dictionary >> at: key ifAbsent: exceptionBlock > keyAtValue: value ifAbsent: exceptionBlock > are certainly often our best friends here - without having the need > for any "real" exceptions (and also too much asking before...) ;-) Again this is not the point, even if I agree on the use of ifAbsent:.... These are idioms and I'm talking here about test coverage! Stef |
In reply to this post by stéphane ducasse-2
> I'm teaching them to write tests and to write tests for a > class that raises exceptions you have to catch them and to > cover the potential behavior of the class! This is not my > wish or not to have exceptions, they are there and to cover > the method behavior (because of different programming > style) I have to cover them. > > For example, what happens if we access an element at not > existing index. Why not have your students write tests like this IntervalTest >> testRemove self should: [(1 to: 3) remove: 2] raise: Exception. [(1 to: 3) remove: 2] on: Exception do: [:ex | self assert: ex class == Error. self assert: ex description = 'elements cannot be removed from an Interval']. This clearly describes what is expected from the Interval class. Michel. |
In reply to this post by Andreas.Raab
On 2 mars 06, at 18:55, Andreas Raab wrote: > stéphane ducasse wrote: >>> Intervall >> remove: anElement >>> self error: 'elements cannot be removed from an Interval' >>> >>> What do you mean with "trapped more specifically"? >> Not for this one >> but >> IndexNotFoundError >> KeyNotFoundError >> SubscriptOutOfBoundsError > > I have often wondered about the usefulness of such hierarchies. The > way I look at it, an exception hierarchy is useful if and only if > one has a need to handle the exceptions individually. Exact! I do not know if people ever tried to catch these ones. I have the impression that this is really an idiom in Smalltalk. In Java you get really a lot of exceptions everywhere. The comparison is interesting. I do not have the taste right now to have strong arguments pros or cons. This is why I asked to see the reaction and improve my exception taste. > And what's interesting here is that in particular the above seem to > be cases where nobody ever handles them (and doesn't have the need > to since there are always variants that explicitly avoid the error > condition like #at:ifAbsent: #removeKey:ifAbsent: etc). Therefore > I'd claim that if you have a case where a client handles an > exception by looking at the error text, then by all means, make it > a separate entity. But otherwise, don't make the system more > complex than it needs to be. Yes. Now the difficulty is to know when you are designing your system in advance how your clients will use it. May be KISS and wait and see. One exception that I like to trap was the "filein error" that you would get when you define a class by sending a message to a superclass that does not exist :). In VW it lets me load broken or partial code that I just need to browse and analyze but not run Stef |
In reply to this post by stéphane ducasse-2
Stef,
> can you read what I wrote.... I'm not teaching anything here > related to first checking if a key exist or not, or > how to use a dictionary...... > > I'm teaching them to write tests and to write tests for a class > that raises exceptions > you have to catch them and to cover the potential behavior of the > class! This is not my wish or not to have exceptions, they are > there and to cover the method behavior (because of different > programming style) I have to cover them. the discussing about exception-handling teaching a bit too hard due to intensive discussions I had with students during _my_ java classes but: > > For example, what happens if we access an element at not existing > index. I think overall the discussion here was quite fruitful! To summarize: 1.) I learned that it makes sense to subclass Error or Exception for testing reasons. Otherwise one does not know if an exception was thrown by the method under test due to a misspelled method name (point of Lukas) or some other method had a problem on the way (point of Julian). 2.) My point is to use one(!) subclass of Error called PreconditionError for all unit tests I call "counter examples": > PreconditionError >> error: aString > > so that above became: > > IntervalTest >> testRemove > self should: [(1 to: 3) remove: 2] raise: PreconditionError I claim that all counter examples check the correct handling of violating a precondition. Using that abstraction also solves the problems above. I invite you all to point out some counter examples for that. ;-) 3.) A very interesting question to ask is if violating a precondition propagates back - meaning that if I call a method foo which calls a method bar, which throws a PreconditionError can be correctly caught with "counter example" for method foo - even if foo does not(!) contain THAT precondition. I think this to be a feature, as otherwise I would have to write the same preconditions all over again. Example: I have a method to solve the quadratic equation f(x)=axx+bx+c Array >> #solveQuadraticEquation |a b c discriminant solutions | self precondition: [self size = 3 and: [self allSatisfy: [:each | each isNumber]]]. "Do I have to state here that the discriminant should be >= 0? I do not think so." a:= self first. b:= self second. c:= self third. discriminant:=(b*b)-(4*a*c). solutions := Set new. solutions add:((0-b+discriminant sqrt)/(2*a)); add:((0-b-discriminant sqrt)/(2*a)). ^solutions If the discriminant<0 the precondition of sqrt should fail. I do not want to state that here also as I am lazy and as I have not computed the discriminant in the beginning. So a "counter example" for solveQuadraticEquation could(!) look like: ArrayTest >> #testSolveQuadraticEquation "The following PreconditionError is _not_ thrown by #solveQuadraticEquation but by sqrt!!!" self should: [#(1 12 37) solveQuadraticEquation] raise: PreconditionError Right now of course sqrt throws a FloatingPointException which is referenced nowhere (!) in the whole system and which does not contain any method at all. This would usually qualify as quite a bogus class. 4.) Actually by using the perspective of PreconditionError I could spot a possible mistake in the implementation of Interval: One should be allowed to remove (and add) the first and last element (s) in an Interval, at least mathematically nothing would speak against it as long as the invariant of representing a "finite arithmetic progression" (class comment of Interval) would be intact. So possibly a better "method test suite" for Interval >>#remove: would contain a "counter example" and a "method test": (names in quotes taken from my unit test classification) IntervalTest >> testRemove |anInterval| self should: [(1 to: 3) remove: 2] raise: PreconditionError anInterval := 1 to: 3. anInterval remove: 3. self assert: (anInterval = (1 to: 2)) 5.) Andreas, Boris and Pascal point was to use subclasses of Exception when you are forced to otherwise programmatically deal with an error string. I agree with this! Andreas and me were cautious about introducing to many exception subclasses. >>> >>> Not for this one >>> but >>> IndexNotFoundError >>> KeyNotFoundError >>> SubscriptOutOfBoundsError We still haven't seen a compelling reason to introduce above exception-classes. I thought you were proposing them but could not find any argument for them. Reading your reply to Andreas it seems that you just wanted to provoke... ;-) As I said, I think you provoked a fruitful discussion. Cheers, Markus |
On Mar 3, 2006, at 12:22 PM, Markus Gaelli wrote: > IntervalTest >> testRemove > |anInterval| > > self should: [(1 to: 3) remove: 2] raise: PreconditionError > anInterval := 1 to: 3. > anInterval remove: 3. > self assert: (anInterval = (1 to: 2)) > > Actually mathematically speaking it is certainly correct to remove 2 from (1 to: 3) as (1 to: 3 by: 2) is an Interval too... ;-) So I correct above "method test suite" into: IntervalTest >> testRemove |anInterval| self should: [(1 to: 4) remove: 2] raise: PreconditionError. anInterval := 1 to: 3. anInterval remove: 2. self assert: (anInterval = (1 to: 3 by: 2)). anInterval := 1 to: 3. anInterval remove: 3; remove: 1; remove: 2. self assert: anInterval isEmpty Cheers, Markus |
Free forum by Nabble | Edit this page |