PhExample, because well designed tests expand on one another (Sneak Preview)

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

PhExample, because well designed tests expand on one another (Sneak Preview)

niko.schwarz
Hi list!

Imagine you want to test the Stack class, and your first test just
creates a test and checks if it's empty.

Then in your second test, you create the same empty stack, but now
push an element 'apple' and check if the size is one.

Then in your third test, you do all the same, and then you pop the
element and check that it is an apple.

Wouldn't it be great if you could write your test cases such that they
could expand on one another? If you didn't need to copy paste the code
of the previous test and just say that you require the previous one!

Well, that's what PhExample lets you do. And as a bonus, tests are
ignored if the test they expand on does not pass. This gives you less
failed tests, because examples are executed only if the examples they
expand on pass too! That gives you the simplest failing examples in
your set to look at!

And best of all, you can run all examples with ye old TestRunner!

The example discussed above would read in code as follows:

EGExample subclass: #ForExampleStack
        …

shouldBeEmpty
        "Create the empty stack"
        | stack |
        stack := Stack new.
        stack should be isEmpty.
        stack size should = 0.
        ^ stack

shouldPushElement
        "Push one element"
        | stack |
        stack := self given: #shouldBeEmpty.
        stack push: 42.
        stack should not be isEmpty.
        stack size should = 1.
        stack top should = 42.
        ^ stack

shouldPopElement
        "And pop it again"
        | stack |
        stack := self given: #shouldPushElement.
        stack pop should = 42.
        stack should be isEmpty.
        stack size should = 0.

Find the code at http://www.squeaksource.com/PhExample.html.

The repository is write-global, so feel free to drop your ideas!

PhExample is based on JExample by Lea Haensenberger, Adrian Kuhn, and
Markus Gaelli. See http://scg.unibe.ch/research/jexample.

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin


2009/10/27 Niko Schwarz <[hidden email]>
The example discussed above would read in code as follows:

EGExample subclass: #ForExampleStack
       …

shouldBeEmpty
       "Create the empty stack"
       | stack |
       stack := Stack new.
       stack should be isEmpty.
       stack size should = 0.
       ^ stack

shouldPushElement
       "Push one element"
       | stack |
       stack := self given: #shouldBeEmpty.

I'd suggest this to be: 

       stack := self given: [self shouldBeEmpty].

This way it's easier to follow what happenning, and refactoring can be applied.
 
       stack push: 42.
       stack should not be isEmpty.
       stack size should = 1.
       stack top should = 42.
       ^ stack
--
Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Adrian Kuhn
Dennis Schetinin <chaetal@...> writes:

>         stack := self given: #shouldBeEmpty.
> I'd suggest this to be: 
>
>         stack := self given: [self shouldBeEmpty].
> This way it's easier to follow what happenning, and refactoring can be applied.

Refactoring should not be a problem, it treats symbols the same way as message
sends. For example, when you rename a method, nut just senders but also all
symbols are renamed.

cheers,
AA



_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin


2009/10/29 Adrian Kuhn <[hidden email]>
Dennis Schetinin <chaetal@...> writes:

>         stack := self given: #shouldBeEmpty.
> I'd suggest this to be: 
>
>         stack := self given: [self shouldBeEmpty].
> This way it's easier to follow what happenning, and refactoring can be applied.

Refactoring should not be a problem, it treats symbols the same way as message
sends. For example, when you rename a method, nut just senders but also all
symbols are renamed.

That's right, my mistake. Anyway IMHO using explicit message sends is preferable, no?

--
Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

George Herolyants-3
2009/10/29 Dennis Schetinin <[hidden email]>:
> That's right, my mistake. Anyway IMHO using explicit message sends is
> preferable, no?

I think not in this case. It looks more clean and declaratively when
you use symbols here.

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin


2009/10/29 George Herolyants <[hidden email]>
2009/10/29 Dennis Schetinin <[hidden email]>:
> That's right, my mistake. Anyway IMHO using explicit message sends is
> preferable, no?

I think not in this case. It looks more clean and declaratively when
you use symbols here.

Using symbols (i.e. "almost" constants) instead of explicitly telling what is happenning (sending a message)  is more clean?! :) Ok then... (Not sure what you mean by "declarative" here)

--
Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

George Herolyants-3
2009/10/29 Dennis Schetinin <[hidden email]>:
> Using symbols (i.e. "almost" constants) instead of explicitly telling what
> is happenning (sending a message)  is more clean?! :) Ok then... (Not sure
> what you mean by "declarative" here)

The message "given:" tells that to you. And by "declarative" I meant
the way you specify dependencies between your tests. If it was done
your way, test would be performed twice. If you use symbol, you don't
have to do it (haven't seen the library yet, just guessing :).

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin


2009/10/29 George Herolyants <[hidden email]>
2009/10/29 Dennis Schetinin <[hidden email]>:
> Using symbols (i.e. "almost" constants) instead of explicitly telling what
> is happenning (sending a message)  is more clean?! :) Ok then... (Not sure
> what you mean by "declarative" here)

The message "given:" tells that to you.

For me it's not clear at all. But this is much more obvious:

        stack := self resultOf: [self shouldBeEmpty].

And by "declarative" I meant
the way you specify dependencies between your tests. If it was done
your way, test would be performed twice.

Why?
 
If you use symbol, you don't
have to do it (haven't seen the library yet, just guessing :).

Don't have to do what? 

--
Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin

... BTW, it's very similar to mocks implementations. The 1-st version was (as in SMocks) something like:

mock expect: #messageSend.
...
self checkExpectations.

But it's much more intension-revealing and convinient to use mocks another way (as in Mocketry for example):

[sut doSomethings] checking: [mock messageSend].

I think in the case of example-driving tests it's still actual...

--
Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

George Herolyants-3
In reply to this post by Dennis Schetinin
2009/10/29 Dennis Schetinin <[hidden email]>:
> For me it's not clear at all. But this is much more obvious:
>
>         stack := self resultOf: [self shouldBeEmpty].
>

So why to use a block then? And stop, why to use "resultOf:"???

>> And by "declarative" I meant
>> the way you specify dependencies between your tests. If it was done
>> your way, test would be performed twice.
>
> Why?

Don't you expect the block to be evaluated?

>>
>> If you use symbol, you don't
>> have to do it (haven't seen the library yet, just guessing :).
>
> Don't have to do what?

Don't have to execute method shouldBeEmpty twice.

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Dennis Schetinin


2009/10/29 George Herolyants <[hidden email]>
2009/10/29 Dennis Schetinin <[hidden email]>:
> For me it's not clear at all. But this is much more obvious:
>
>         stack := self resultOf: [self shouldBeEmpty].
>

So why to use a block then? And stop, why to use "resultOf:"???

For the same reason the execution of the test was being wrapped by original #given: (maybe we have to ask author?)
 
 
>> And by "declarative" I meant
>> the way you specify dependencies between your tests. If it was done
>> your way, test would be performed twice.
>
> Why?

Don't you expect the block to be evaluated?

>>
>> If you use symbol, you don't
>> have to do it (haven't seen the library yet, just guessing :).
>
> Don't have to do what?

Don't have to execute method shouldBeEmpty twice.

Sorry, I don't understand. My variant was exactly the same as (unknown:) original one but using explicit message send instead of hiding it by selector passing. Including number of #shouldBeEmpty executions.


--

Dennis Schetinin

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: PhExample, because well designed tests expand on one another (Sneak Preview)

Adrian Kuhn
In reply to this post by George Herolyants-3
George Herolyants <george.herolyants@...> writes:

> 2009/10/29 Dennis Schetinin <chaetal@...>:
> > Using symbols (i.e. "almost" constants) instead of explicitly telling what
> > is happenning (sending a message)  is more clean?! :) Ok then... (Not sure
> > what you mean by "declarative" here)
>
> The message "given:" tells that to you. And by "declarative" I meant
> the way you specify dependencies between your tests. If it was done
> your way, test would be performed twice. If you use symbol, you don't
> have to do it (haven't seen the library yet, just guessing :).

Genau.

By the principle of least surprise (of which I am also a big supporter) one
would expect to just being able to call the perquisites. However in our case we
need (and want) declarative dependencies, such that the framework may interfere
between "producing" and "consuming" test. If tests would directly call one an
other, as suggested, we cannot interfere.

Being able to interfere "calls" between tests allows the framework to control
of how tests are executed. We use this to skip tests that depend on failed
tests. Eg, if you introduce a failure in ForExampleStack >> #shouldPush, what
you get are not 4 failures but 1 failure and 3 expected failures!

Also, interfering "calls" between tests allows the framework to choose a
strategy for multiple dependencies. If two or more tests depend on the same
produce we can: a) run the producer n times or b) run the producer once,
cache the result and clone it n times. Currently we simply rerun the tests,
but for the future it should be possible to choose between these two
strategies. My experience from JExample is that rerunning is often faster,
since initializing objects is much faster then cloning them, I expect this
tradeoff might differ in Smalltalk though :)

cheers,
AA





_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Phexample moved to new account on Squeaksource

Adrian Kuhn
In reply to this post by niko.schwarz
Dear list,

in the future please use the script to load Phexample

        Gofer new
                squeaksource: 'phexample';
                addPackage: 'Phexample-Core';
                load.

we renamed the URL to be all lower case.

PS, as a goodie you will find a glamour browser in the package
'Phexample-Glamour', critical feedback welcome!

cheers,
AA


_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project