Agreed. A little knowledge of how it is to be used can go a long way.

I seem to have a mental block on creating traits. I am sure my code and
applications would benefit - but I just can’t seem to understand what they
are and how to  design/implement them.

Russ Whaley
whaley.r...@gmail.com


On Tue, Feb 4, 2025 at 2:23 AM Richard O'Keefe <rao...@gmail.com> wrote:

> I too like to use random data in test cases but you also have to use
> edge cases that random data tend to avoid.
> It is painful to recall the millions of successful tests of my
> Dictionary implementation that failed to detect that
> "Dictionary new: 0" created a non-functional object.  ((self pvtHash:
> key) \\ capacity) + 1" did not work well when
> capacity was 0. 0 size, 0 capacity, 0 room left, any edge case.
> Conversely, you have to avoid special cases
> sometimes: when testing x ./ y and y \ x for Quaternions, you want to
> make sure y isZero not.
>
> One thing I would like some advice on is testing traits.  I understand
> about testing classes and about testing
> methods in classes (though I am never thorough enough), but how do you
> test a method that isn't bound to
> a particular context, where the thing that reveals a mistake may be a
> class it will be combined with in the
> future that doesn't exist yet?  What is considered good practice?
>
> On Tue, 4 Feb 2025 at 05:25, Russ Whaley <whaley.r...@gmail.com> wrote:
> >
> > Folks, I really appreciate (and learn from) these types of conversations
> - especially on testing where I struggle for consistency and completeness.
> >
> > I tend to agree that my testing focuses on methods, however the majority
> of my ‘failures’ revolve around insufficient object values that eventually
> have my tests failing - I’ve defaulted to generating ‘data’ as randomly as
> I can early on, however non-realistic, and running tests a few thousand
> times to point out assumptions I make in my methods.
> >
> > Thanks for discussions!
> >
> > Russ Whaley
> > whaley.r...@gmail.com
> >
> >
> > On Thu, Jan 23, 2025 at 5:02 AM Richard O'Keefe <rao...@gmail.com>
> wrote:
> >>
> >> This makes the assumption that there is, most of the time, a single,
> >> unique, object that the test is clearly about.
> >> I hasten to admit that my unit testing practice needs a lot to be
> desired.
> >> But it has not been my experience that there *is* any such object much
> >> of the time.
> >> For example, to test a method, I will sometimes create two equal
> >> objects, manipulate one of them in the
> >> old trusted way, manipulate the other in the new way, and then see if
> >> the objects are equal again.
> >> Which then is *the* subject?
> >> The answer is neither, because what the test is *about* is not an
> >> object, but a method.
> >> Or again, I open a stream to a new file, create a complex object,
> >> persist it on the stream, close
> >> the stream, open another stream reading from the file, reconstitute an
> >> object from it, and then
> >> check that the two objects are isomorphic.  Which is *the* subject?
> >> Neither, because what I am testing is the encoding and decoding
> >> methods, not the objects.
> >>
> >> The key paragraph is this one:
> >> <quote>
> >> One could argue that there is not always a single object under test.
> >> In some situations that is correct, for instance when testing the
> >> interaction between objects. But in my experience, most unit tests
> >> have a single object under test.
> >> </quote>
> >>
> >> And it is *this* that we really need a blog post on.  I am very ready
> >> to believe that you are a much better tester than I am.
> >> (That wouldn't be hard.)  But *something* interesting is going on when
> >> I think in terms of testing classes and methods to
> >> the point where I find the idea of testing a particular object almost
> >> incomprehensible, yet you think this is the norm.
> >> I take some comfort from the fact that the Gherkin language in
> >> Cucumber (and why oh which has nobody bothered to
> >> write down a proper grammar, let alone even an informal semantics, for
> >> it?  How am I supposed to know what is and what
> >> is not legal Gherkin?)  goes out of its way to conceal the existence
> >> of subjects, should they exist, from test reviewers, and
> >> calls what a test is about a "feature".
> >>
> >> Here are two tests for the Quaternion class:
> >>     testMultiplicationByScalarCommutes
> >>       sampleExactScalars do: [:x |
> >>         sampleExactQuaternions do: [:y |
> >>           self assert: (x*y) = (y*x)]].
> >>
> >>     testMultiplicationOfPowersCommutes
> >>       sampleExactQuaternions do: [:x |
> >>         |y z|
> >>         y := x squared.
> >>         z := x cubed.
> >>         self assert: (y*z) = (z*y)].
> >>
> >> These two methods test important properties of the #* method.
> >>
> >> Here's another one, part of testing the test data..
> >>
> >>     testExactScalarsAreExactScalars
> >>       sampleExactScalars do: [:each |
> >>         self assert: each isScalar.
> >>         self assert: each isExact].
> >>
> >> This tests the *methods* #isScalar and #isExact, and indirectly the
> >> code that generates the test data.
> >> It's not about any particular object.  Nor is it about *one* method.
> >> It's very hard to write a test case that
> >> tests only one method.  In fact my own rather embarrassing experience
> >> is that when a test case of mine
> >> failed it quite often wasn't the method I thought I was testing that
> >> was wrong.  Returning to the
> >> persistence example, it's not testing #saveOn: (object saveOn:
> >> byteStream) or #restore (byteStream restore).
> >> It's testing that the two methods are properly related.  It would be
> >> wrong to call it #testSaveOn: and it
> >> would be wrong to call it #testRestore.  There's a distinction here:
> >> it *is* possible to write methods to test
> >> #saveOn: by itself, by writing various objects and checking that you
> >> get the right bytes.  But if the external
> >> representation is changed (and I need to change it), the tests for
> >> #saveOn: and #restore as such will have
> >> to be rewritten, while the tests for #saveOn: and #restore *together*
> will not.
> >>
> >> Cucumber offers an insight here, I think.  A scenario in Gherkin is
> >> not a test case for an object.
> >> Nor is it a test case for a method.  It is a test case for a composite
> >> *behaviour* of  a system,
> >> at some contextually appropriate level of detail.
> >>
> >> Brian Marick had some lovely stickers.  One of them reads "An example
> >> would be handy about now."
> >>
> >> I note that your blog post *did* provide an example of testing Date.
> >> In which none of your test cases used 'subject'.
> >> I entirely agree with you that the three methods have good
> >> intention-revealing names.
> >> What we actually need is some examples where 'subject' pays off.
> >>
> >>
> >>
> >> On Mon, 20 Jan 2025 at 04:04, Koen De Hondt
> >> <k...@all-objects-all-the-time.st> wrote:
> >> >
> >> > Dear Pharo users and developers,
> >> >
> >> > In my series of blog posts on testing, I added a follow-up post about
> a dedicated name for the object under test in SUnit tests, the “subject".
> >> >
> >> > Happy reading!
> >> >
> >> > Ciao,
> >> > Koen
> >> >
>

Reply via email to