On Mar 29, 2006, at 7:35 PM, Markus Gaelli wrote: > On the other hand some philosophers like Wittgenstein or linguists > like Lakoff would say that there are better and worse examples for > a given concept - you would not explain the concept of a bird to a > child with a penguin first... > > So how do I make explicit which "animal" I am really interested in > a test? > > Again, just use the block concept. > > FooTest >> testBar > "Our tool is actually able to detect that this is an InverseTest" > > "..someSetupCode for blaBla." > (...) > self test: [aResult := blaBla bar inverseBar] "All methods called > in the test block are methods under test" > > "some assertions to make this a test and not a mere example" > self assert: (aResult = blaBla) Having said all this, I have to retract my arguments vs. self shouldnt:[bb copyBits] raise: Error. from my morning mail of course... If its always Error that I don't expect in my methods under test I could rewrite > self test: [aResult := blaBla bar inverseBar] "All methods called > in the test block are methods under test" from above easily into self shouldntRaiseError: [aResult := blaBla bar inverseBar] "All methods called in the test block are methods under test" ;-) The only difference to Andreas' solution would be that I would bracket the methods under test like this, even if there are some assertions down below in the code. Cheers, Markus |
In reply to this post by Lukas Renggli
--- In [hidden email], "Lukas Renggli" <renggli@...> wrote:
> > > I think that Diego is saying that it is better not to test the > > implementation but the behavior of an object. If the implementation > > changes and you have implementation tests, you will have to change > > them. On the other hand, if you only test behavior and you change the > > implementation you should not change the tests. > > But this is the same, if you change the behavior or interface, you > have to adapt the tests as well, so this does not count ;-) > Ha, ha, this is a good one... but only if you take it out of context... Of course that if B depends on A an A changes, then B will have to change, that is not the issue. I think the issue here is: "What's more important, implementation or behavior?" and I beleive the answer is behavior. Implementation can change many times but the responsibilities of an object, its essence, should change less that its implementation... (I'm not saying that is always like this, but that's the idea...) If I have tests based on behavior, then I can reuse them for different implementations, for example, a Stack could be implemented with a List, with an Array, etc., there could even be the two implementations, but I could use the same tests... Anyway, having said this, I guess I can answers your "so this does not count ;-)" with a "so this does not count ;-)" ;-) (hey, just kidding, don't get mad!). > > I understand that you would want to see that your implementation is > > right, but is not enough to test the behavior? what implementation > > mistake couldn't you find testing the behavior? > > You are probably right for most cases. > > How do you test class invariants in your test-cases, without exposing > the internal state of the object? Well, I beleive that an object's invariant is based on its behavior, not its implementation. For example, in a CircularList the last and first elements are linked (if there are elements of course). That's the invariant no matter if the CircularList uses a DoubleLinkedList, an OrderedCollection or whatever structure you want to use to implement the circular list. I don't think you have to expose the internal state of an object to test its invariant... The idea of invariant, as I understand it, it is tied to the concept itself and if you want to test the invariant, that should happend all the time something changes in the instance of that concept. That's what Eiffel does, but in Eiffel you write the invariant as part of the class not as a separate test. [snip] > > We have talked with Diego many times about annotations and our opinion > > is that annotations are a tool that do not follow the idea of having > > only "objects and messages". It is sintax sugar that makes the > > language more difficult to use. It is a matter of taste and I don't > > like sugar :-)... (For sure you have discuss this on the list many > > times...) > > - An annotation is an object, as everything is. > > - An annotation is a message(-send), you can send it to any object and > have behavior and/or a return objects as an effect. I have to admit that I have not read about annotations, just saw a few examples, but I'd like you to think about this: 1) When I see an annotation I don't see an object, neither a message not a message send, I just see "<anAnnotation>". 2) This means that I need to learn something new that is not about objects and messages (at least explicitly) but about sintax. This may be a good price to pay, but only if this new tool gives me something it is hard to do in other ways. When I see an annotation I see a "tag" (an this is personal, maybe if I read more about them I'll see other things), wich remembers me and transports me to the "relational paradigm", where everytime you need to "tag" something you just create a new "field" on the "row". But, what is the essence of doing this?, the essence is to create a categorization. In the case of annotations, to categorize a method using different "tags". Well, I beleive categorization can be achieved in a much simple and flexible way using sets (I mean set in the abstract way) which in the "relational paradigm" would mean to create a new table, and in the case of tests using SUnit, to create different suites. Forgive my ignorance, but how would you categorize a test of beeing, let's say, an architecture test and an integration test with annotations? I want to be able to run only architecture tests, or integration tests, or both or neither. I cant think of two ways of anotating: 1) aClass>>test1 <architectureTest> <integrationTest> ... the test 2) aClass>>test1 <architectureTest> <integrationTest> <architectureTestAndIntegrationTest> ... the test But I don't kwnow how to obtain which ones are architecture tests, etc. Using sets and suites, it is simple: Set architectureTests := Set with: aClass>>test1 with: ... Set integrationTests := Set with: aClass>>test1 with: ... Set archtectureAndIntegrationTests := architectureTests union: integrationTests. architectureTestSuite := (TestSuite named:..) addTests: architectureTests. integrationTestSuite := (TestSuite named:..) addTests: integrationTests. etc. If I need to change the categorization, I change the objects (the sets), not a method, and the info about the categories is in one place, not spread all over. Bye, Hernan. |
> I cant think of two ways of anotating:
> 1) > aClass>>test1 > <architectureTest> > <integrationTest> > ... the test > > 2) > aClass>>test1 > <architectureTest> > <integrationTest> > <architectureTestAndIntegrationTest> > ... the test > > But I don't kwnow how to obtain which ones are architecture tests, > etc. > Using sets and suites, it is simple: > > Set architectureTests := Set with: aClass>>test1 with: ... > Set integrationTests := Set with: aClass>>test1 with: ... > Set archtectureAndIntegrationTests := architectureTests union: > integrationTests. > > architectureTestSuite := (TestSuite named:..) addTests: architectureTests. > integrationTestSuite := (TestSuite named:..) addTests: integrationTests. > etc. > > If I need to change the categorization, I change the objects (the > sets), not a method, and the info about the categories is in one > place, not spread all over. Mhh, I see that different ... ;-) The problem with your approach is that you categorize your methods at a different places than you define them. Basically this means I have to accept two methods, if I add, change or remove a test. I do very much prefer your annotation example (1) where I directly tag the concerning method with the necessary meta-information. For me this is much easier to maintain, because I can see immediately when browsing the method to what category it belongs to. Moreover I can quickly query all the currently taged methods by browsing the senders of #architectureTest. Then I get the suite with one line of code, not a method full of selectors defined somewhere throughout the image: Set architectureTests := MethodAnnotation allNamed: #architectureTest from: aClass. Cheers, Lukas -- Lukas Renggli http://www.lukas-renggli.ch |
Lukas Renggli wrote:
>Mhh, I see that different ... ;-) > >The problem with your approach is that you categorize your methods at >a different places than you define them. Basically this means I have >to accept two methods, if I add, change or remove a test. > >I do very much prefer your annotation example (1) where I directly tag >the concerning method with the necessary meta-information. For me this >is much easier to maintain, because I can see immediately when >browsing the method to what category it belongs to. Moreover I can >quickly query all the currently taged methods by browsing the senders >of #architectureTest. Then I get the suite with one line of code, not >a method full of selectors defined somewhere throughout the image: > >Set architectureTests := MethodAnnotation allNamed: #architectureTest >from: aClass. > > I have to agree on that, it is much easier the way you propose... -- ______________________________ Lic. Hernán A. Wilkinson Gerente de Desarrollo y Tecnología Mercap S.R.L. Tacuari 202 - 7mo Piso - Tel: 54-11-4878-1118 Buenos Aires - Argentina http://www.mercapsoftware.com --------------------------------------------------------------------- Este mensaje es confidencial. Puede contener informacion amparada por el secreto profesional. Si usted ha recibido este e-mail por error, por favor comuniquenoslo inmediatamente via e-mail y tenga la amabilidad de eliminarlo de su sistema; no debera copiar el mensaje ni divulgar su contenido a ninguna persona. Muchas gracias. This message is confidential. It may also contain information that is privileged or otherwise legally exempt from disclosure. If you have received it by mistake please let us know by e-mail immediately and delete it from your system; you should also not copy the message nor disclose its contents to anyone. Thanks. --------------------------------------------------------------------- |
> I have to agree on that, it is much easier the way you propose...
Gosh, really? I didn't yet brought up the best argument ... ;-) Imagine several packages with tests being categorized as #architectureTests and #integrationTests. Now not all the developers want to always load all the packages (e.g. maybe packages provide extensions you don't need in your particular image, or there are extra packages of other people not under your control). Defining a global method returning all the #architecutreTests simply doesn't work with such a setup, querying meta information of methods however does. This is exactly the problem I was confronted with when developing Magritte/Pier, and this is the reason why I implemented and itroduced MethodAnnotations for Squeak 3.9. Thanks for the interesting discussion! Cheers, Lukas -- Lukas Renggli http://www.lukas-renggli.ch |
Free forum by Nabble | Edit this page |