About use of specific error

Markus Gaelli gaelli at emergent.de
Fri Mar 3 11:22:58 UTC 2006


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.
Agreed already yesterday. As I said/ wanted to say: Maybe I pushed  
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



More information about the Squeak-dev mailing list