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 > > >