[squeak-dev] Specifying with a TestCase

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Specifying with a TestCase

Nicolas Cellier-3
I want to relate here a discussion taking place at
http://bugs.squeak.org/view.php?id=6347
Thanks to Norbert Hartl for his collaboration and time devoted to it.

The subject is how to write a system specification TestCase.
The example taken from the bug report is to clearly express this rule:

"For every pair (o1,o2) of equal objects (o1=o2),
of different identity (o1~~o2),
registering an action for each object of the pair at finalization,
shall trigger both actions upon finalization."

I put here a new attempt with self documenting code:

finalizationProbe := Set new.
o1 := 'hello' copy.
o2 := 'hello' copy.
forAnyTwoEqualObjects := [o1 = o2].
ofDifferentIdentity := [o1 ~~ o2].
registeringAnActionAtFinalizationForEachObject := [
        o1 toFinalizeSend: #add: to: finalizationProbe
                with: 'first hello finalized'.
        o2 toFinalizeSend: #add: to: finalizationProbe
                with: 'second hello finalized'].
thenForcingFinalizationOfObjects := [
        o1 := o2 := nil. Smalltalk garbageCollect].
implyBothRegisteredActionsAreExecuted := [finalizationProbe size = 2].

self
        assert: forAnyTwoEqualObjects;
        assert: ofDifferentIdentity;
        should: [
                registeringAnActionAtFinalizationForEachObject value.
                thenForcingFinalizationOfObjects value.
                implyBothRegisteredActionsAreExecuted value].

Preamble longer than pure English, and the #value are a bit parasitic...
And why using leading assertions? They are not the goal of the test...

Well, the TestCase don't prove the implication by itself.
It just tells whether verified or not for some objects.

Assertions are there to clearly states that the rule is not meant to be
casually true for some objects, but shall apply to any objects verifying
the pre-condition.
They are the left member of the implication we want to verify.
They are also self-protecting the TestCase (if ever the two objects
chosen become identical or not equal after a refactoring).
I think they have their place here.

Trusting the comments does work as well and leads to a shorter code:

"For every pair of equal objects of different identity,"
        o1 := 'hello' copy.
        o2 := 'hello' copy.
"when both are registering an action at finalization,"
        finalizationProbe := Set new.
        o1 toFinalizeSend: #add: to: finalizationProbe
                with: 'first hello finalized'.
        o2 toFinalizeSend: #add: to: finalizationProbe
                with: 'second hello finalized'.
        self assert: finalizationProbe size = 0.
"upon finalization..."
        o1 := o2 := nil. Smalltalk garbageCollect
"...both actions shall be triggered"
        self assert: finalizationProbe size = 2.

Or see excellent variation from Norbert with a pair. Small is beautiful.
But notions of equal objects (o1 = o2) and different identity (o1 ~~ o2)
are very much implicit here.
Good for readability, but is it a clear rationale for using
IdentityDictionary?

Sure, the whole discussion is overkill!
Regarding productivity, including time to discuss how to write tests,
rules shall better not change too fast! Reserved to a stable Kernel...
Or a Smalltalk academy rewriting the Kernel every 100 years or so.
I wonder if we can specify a whole Smalltalk Kernel like this by the
way? (If we cannot specify a method, then throw it away!).

I wish this example serve as a mirror to reflect our own practices.
Do we really write TestCase with specifications in mind?
Can they serve as a rationale for an implementation?
Which readers are the TestCase addressed to?
Not sure i can understand every TestCase intention in the image...
Shouldn't I?

Nicolas


Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: Specifying with a TestCase

Nicolas Cellier-3
HeHe, forgot the declaration of temps:

| finalizationProbe o1 o2
        forAnyTwoEqualObjects
        ofDifferentIdentity
        registeringAnActionAtFinalizationForEachObject
        thenForcingFinalizationOfObjects
        implyBothRegisteredActionsAreExecuted |

are Worth a method comment!

nicolas cellier a écrit :

> I want to relate here a discussion taking place at
> http://bugs.squeak.org/view.php?id=6347
> Thanks to Norbert Hartl for his collaboration and time devoted to it.
>
> The subject is how to write a system specification TestCase.
> The example taken from the bug report is to clearly express this rule:
>
> "For every pair (o1,o2) of equal objects (o1=o2),
> of different identity (o1~~o2),
> registering an action for each object of the pair at finalization,
> shall trigger both actions upon finalization."
>
> I put here a new attempt with self documenting code:
>
> finalizationProbe := Set new.
> o1 := 'hello' copy.
> o2 := 'hello' copy.
> forAnyTwoEqualObjects := [o1 = o2].
> ofDifferentIdentity := [o1 ~~ o2].
> registeringAnActionAtFinalizationForEachObject := [
>     o1 toFinalizeSend: #add: to: finalizationProbe
>         with: 'first hello finalized'.
>     o2 toFinalizeSend: #add: to: finalizationProbe
>         with: 'second hello finalized'].
> thenForcingFinalizationOfObjects := [
>     o1 := o2 := nil. Smalltalk garbageCollect].
> implyBothRegisteredActionsAreExecuted := [finalizationProbe size = 2].
>
> self
>     assert: forAnyTwoEqualObjects;
>     assert: ofDifferentIdentity;
>     should: [
>         registeringAnActionAtFinalizationForEachObject value.
>         thenForcingFinalizationOfObjects value.
>         implyBothRegisteredActionsAreExecuted value].
>
> Preamble longer than pure English, and the #value are a bit parasitic...
> And why using leading assertions? They are not the goal of the test...
>
> Well, the TestCase don't prove the implication by itself.
> It just tells whether verified or not for some objects.
>
> Assertions are there to clearly states that the rule is not meant to be
> casually true for some objects, but shall apply to any objects verifying
> the pre-condition.
> They are the left member of the implication we want to verify.
> They are also self-protecting the TestCase (if ever the two objects
> chosen become identical or not equal after a refactoring).
> I think they have their place here.
>
> Trusting the comments does work as well and leads to a shorter code:
>
> "For every pair of equal objects of different identity,"
>     o1 := 'hello' copy.
>     o2 := 'hello' copy.
> "when both are registering an action at finalization,"
>     finalizationProbe := Set new.
>     o1 toFinalizeSend: #add: to: finalizationProbe
>         with: 'first hello finalized'.
>     o2 toFinalizeSend: #add: to: finalizationProbe
>         with: 'second hello finalized'.
>     self assert: finalizationProbe size = 0.
> "upon finalization..."
>     o1 := o2 := nil. Smalltalk garbageCollect
> "...both actions shall be triggered"
>     self assert: finalizationProbe size = 2.
>
> Or see excellent variation from Norbert with a pair. Small is beautiful.
> But notions of equal objects (o1 = o2) and different identity (o1 ~~ o2)
> are very much implicit here.
> Good for readability, but is it a clear rationale for using
> IdentityDictionary?
>
> Sure, the whole discussion is overkill!
> Regarding productivity, including time to discuss how to write tests,
> rules shall better not change too fast! Reserved to a stable Kernel...
> Or a Smalltalk academy rewriting the Kernel every 100 years or so.
> I wonder if we can specify a whole Smalltalk Kernel like this by the
> way? (If we cannot specify a method, then throw it away!).
>
> I wish this example serve as a mirror to reflect our own practices.
> Do we really write TestCase with specifications in mind?
> Can they serve as a rationale for an implementation?
> Which readers are the TestCase addressed to?
> Not sure i can understand every TestCase intention in the image...
> Shouldn't I?
>
> Nicolas
>
>
>