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 |
2009/10/27 Niko Schwarz <[hidden email]> Dennis SchetininThe example discussed above would read in code as follows: 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. -- _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
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 |
2009/10/29 Adrian Kuhn <[hidden email]>
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 |
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 |
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) Dennis Schetinin _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
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 |
2009/10/29 George Herolyants <[hidden email]>
For me it's not clear at all. But this is much more obvious: stack := self resultOf: [self shouldBeEmpty]. And by "declarative" I meant Why? If you use symbol, you don't 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 |
... 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 |
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 |
2009/10/29 George Herolyants <[hidden email]> 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.
For the same reason the execution of the test was being wrapped by original #given: (maybe we have to ask author?)
-- Dennis Schetinin _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
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 |
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 |
Free forum by Nabble | Edit this page |