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