[squeak-dev] The Inbox: SUnit-ct.127.mcz

Jakob Reschke forums.jakob at resfarm.de
Sat Apr 11 18:42:24 UTC 2020


I once had an image with an assert:includes: in it. Guess how
discontent I was when I found out that I had been using an extension,
not a core feature.

I just looked up xUnit (surprisingly they don't have an API reference,
so I had to look in the code), and their Assert class is a partial
one, and the various methods come from separate files such as
StringAsserts. Moreover regular expressions are in the core .NET
framework, are they? They are also in Squeak Trunk, but the package
could be unloaded.

There is another difference to be noted: in xUnit, as in JUnit since
4, there is no TestCase class to hold assertion methods. Rather, test
classes are plain classes and they call other classes that implement
assertions (as static methods). And of course you can and are
encouraged to add more Assert-like classes.

We have no Assert-like classes, at least not in Trunk (or do we?), but
if we should not add any, I think any additional assert methods should
at least be marked as "extension" either by putting them in that
category as you said Christoph, or by being extension methods of a
different package (RegexSUnit?)

Personally, I mix in my custom assertion methods that I need in
multiple classes via Traits that I use in the concrete test classes.



Am Sa., 11. Apr. 2020 um 15:45 Uhr schrieb Thiede, Christoph
<Christoph.Thiede at student.hpi.uni-potsdam.de>:
>
> Hi all, thanks for your feedback! :-)
>
>
> Originally, I wrote these assertions for testing the refactored inspectors, only. Marcel suggested me to push them up into TestCase and in general, I liked the idea because it would save every new specific test suite from implementing the same stuff again whenever desired. On the other hand, I share yours (Chris) fears that this approach could turn TestCase into a god class for everything that can be asserted in any way. Analogously, you could introduce #assert:lowerThan:, #assert:includes: and much more domain-specific assertion. I don't know where to draw the line.
>
>
> Other test frameworks such as python's unittest or XUnit for .NET declare a lot of domain-specific assertions (for system domains such as regex, collections). Is this a desirable goal?
>
> On the one hand, it maximizes the convenience of the test developer in terms of writing or reading the test code or getting comprehensive assertion failures.
>
> On the other hand, this approach can cause a lot of duplication what is quite the opposite of Smalltalk's minimalism which I do appreciate very much and always strive to preserve.
>
>
> @Jakob: Oh, that compatibility aspect is good to know. I suppose I now understand the sense of the SUnitExtensions separation. I suppose #assert:matches: should belong into the "extensions" category of TestCase, and the tests into SUnitExtensionsTest (provided that we wanted the feature at all, see above)?
>
>
> Best,
>
> Christoph
>
> ________________________________
> Von: Squeak-dev <squeak-dev-bounces at lists.squeakfoundation.org> im Auftrag von Chris Muller <asqueaker at gmail.com>
> Gesendet: Samstag, 11. April 2020 03:56:36
> An: The general-purpose Squeak developers list
> Betreff: Re: [squeak-dev] The Inbox: SUnit-ct.127.mcz
>
> I share Jakob's sentiments on this, and would wonder whether this particular kind of domain-specific matching is an appropriate responsibility for SUnit.  Currently, it's 100% client responsibility to calculate assertions for SUnit.  Blending this responsibility into SUnit means it could drift over time, causing tests that once passed to no longer passed (if they depended on these methods).
>
> On Fri, Apr 10, 2020 at 4:30 AM Jakob Reschke <forums.jakob at resfarm.de> wrote:
>>
>> Please be aware that the basic SUnit is not specific to Squeak and
>> enlarging its interface makes it even more unlikely that the written
>> test cases will be portable.
>>
>> http://sunit.sourceforge.net/
>>
>> Although I am afraid that compatibility might not maintained any more already.
>> Nevertheless one could at least put the additional methods in an
>> extensions package, which could as well be in Squeak Trunk.
>>
>> Am Do., 9. Apr. 2020 um 14:23 Uhr schrieb <commits at source.squeak.org>:
>> >
>> > Christoph Thiede uploaded a new version of SUnit to project The Inbox:
>> > http://source.squeak.org/inbox/SUnit-ct.127.mcz
>> >
>> > ==================== Summary ====================
>> >
>> > Name: SUnit-ct.127
>> > Author: ct
>> > Time: 9 April 2020, 2:22:49.769949 pm
>> > UUID: bf5be456-fbd7-e343-9158-da59c75fa05d
>> > Ancestors: SUnit-ct.126
>> >
>> > Adds support for string pattern assertions (#assert:matches:[description:] and #deny:matches:[description:]). Also tests these selectors in SUnitTest.
>> >
>> > TestCase new assert: 'Sq*k' matches: 'Squeak'.
>> > TestCase new assert: 'Sq\w+k' asRegex matches: 'Squeak'.
>> > TestCase new deny: '.*\d' asRegex matches: 'Squeak123' description: 'This one will fail'.
>> >
>> > Depends indeed on SUnit-ct.126.
>> >
>> > =============== Diff against SUnit-ct.126 ===============
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testAssertMatches (in category 'tests') -----
>> > + testAssertMatches
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'f*'.
>> > +       positive := 'foo'.
>> > +       negative := 'bar'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self assert: pattern matches: positive] raise: TestFailure.
>> > +
>> > +       self should: [self assert: pattern matches: negative] raise: TestFailure.
>> > +       [self assert: pattern matches: negative]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern)
>> > +                               description: 'Error message doesn''t include the expected pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: negative)
>> > +                               description: 'Error message doesn''t include the actual value'].
>> > +
>> > +       self should: [self assert: pattern matches: notAString] raise: TestFailure.
>> > +       [self assert: pattern matches: notAString]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern)
>> > +                               description: 'Error message doesn''t include the expected pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: notAString asString)
>> > +                               description: 'Error message doesn''t include the actual value'].!
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testAssertMatchesDescription (in category 'tests') -----
>> > + testAssertMatchesDescription
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'f*'.
>> > +       positive := 'foo'.
>> > +       negative := 'bar'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self assert: pattern matches: positive description: ['A description' , 42]] raise: TestFailure.
>> > +
>> > +       self should: [self assert: pattern matches: negative description: ['A description' , 42]] raise: TestFailure.
>> > +       [self assert: pattern matches: negative description: ['A description' , 42]]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'A description' , 42)
>> > +                               description: 'Error message doesn''t give you the description'].
>> > +
>> > +       self should: [self assert: pattern matches: notAString description: ['A description' , 42]] raise: TestFailure.
>> > +       [self assert: pattern matches: notAString description: ['A description' , 42]]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'A description' , 42)
>> > +                               description: 'Error message doesn''t give you the description'].!
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testAssertMatchesRegex (in category 'tests') -----
>> > + testAssertMatchesRegex
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'fo+' asRegex.
>> > +       positive := 'foo'.
>> > +       negative := 'f'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self assert: pattern matches: positive] raise: TestFailure.
>> > +
>> > +       self should: [self assert: pattern matches: negative] raise: TestFailure.
>> > +       [self assert: pattern matches: negative]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern asString)
>> > +                               description: 'Error message doesn''t include the expected regex pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: negative)
>> > +                               description: 'Error message doesn''t include the actual value'].
>> > +
>> > +       self should: [self assert: pattern matches: notAString] raise: TestFailure.
>> > +       [self assert: pattern matches: notAString]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern asString)
>> > +                               description: 'Error message doesn''t include the expected regex pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: notAString asString)
>> > +                               description: 'Error message doesn''t include the actual value'].!
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testDenyMatches (in category 'tests') -----
>> > + testDenyMatches
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'f*'.
>> > +       positive := 'foo'.
>> > +       negative := 'bar'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self deny: pattern matches: negative] raise: TestFailure.
>> > +
>> > +       self should: [self deny: pattern matches: positive] raise: TestFailure.
>> > +       [self deny: pattern matches: positive]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern)
>> > +                               description: 'Error message doesn''t include the expected pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: positive)
>> > +                               description: 'Error message doesn''t include the actual value'].
>> > +
>> > +       self should: [self deny: pattern matches: notAString] raise: TestFailure.
>> > +       [self deny: pattern matches: notAString]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern)
>> > +                               description: 'Error message doesn''t include the expected pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: notAString asString)
>> > +                               description: 'Error message doesn''t include the actual value'].!
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testDenyMatchesDescription (in category 'tests') -----
>> > + testDenyMatchesDescription
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'f*'.
>> > +       positive := 'foo'.
>> > +       negative := 'bar'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self deny: pattern matches: negative description: ['A description' , 42]] raise: TestFailure.
>> > +
>> > +       self should: [self deny: pattern matches: positive description: ['A description' , 42]] raise: TestFailure.
>> > +       [self deny: pattern matches: positive description: ['A description' , 42]]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'A description' , 42)
>> > +                               description: 'Error message doesn''t give you the description'].
>> > +
>> > +       self should: [self deny: pattern matches: notAString description: ['A description' , 42]] raise: TestFailure.
>> > +       [self deny: pattern matches: notAString description: ['A description' , 42]]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'A description' , 42)
>> > +                               description: 'Error message doesn''t give you the description'].!
>> >
>> > Item was added:
>> > + ----- Method: SUnitTest>>testDenyMatchesRegex (in category 'tests') -----
>> > + testDenyMatchesRegex
>> > +
>> > +       | pattern positive negative notAString |
>> > +       pattern := 'fo+' asRegex.
>> > +       positive := 'foo'.
>> > +       negative := 'f'.
>> > +       notAString := Object new.
>> > +
>> > +       self shouldnt: [self deny: pattern matches: negative] raise: TestFailure.
>> > +
>> > +       self should: [self deny: pattern matches: positive] raise: TestFailure.
>> > +       [self deny: pattern matches: positive]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern asString)
>> > +                               description: 'Error message doesn''t include the expected regex pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: negative)
>> > +                               description: 'Error message doesn''t include the actual value'].
>> > +
>> > +       self should: [self deny: pattern matches: notAString] raise: TestFailure.
>> > +       [self deny: pattern matches: notAString]
>> > +               on: TestFailure do: [:ex |
>> > +                       | error |
>> > +                       error := ex messageText.
>> > +                       self
>> > +                               assert: (error includesSubstring: 'string')
>> > +                               description: 'Error message doesn''t say that we''re passing a non-string object here'.
>> > +                       self
>> > +                               assert: (error includesSubstring: pattern asString)
>> > +                               description: 'Error message doesn''t include the expected regex pattern'.
>> > +                       self
>> > +                               assert: (error includesSubstring: notAString asString)
>> > +                               description: 'Error message doesn''t include the actual value'].!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>assert:matches: (in category 'accessing') -----
>> > + assert: pattern matches: actual
>> > +
>> > +       ^ self
>> > +               assert: pattern
>> > +               matches: actual
>> > +               description: nil!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>assert:matches:description: (in category 'accessing') -----
>> > + assert: pattern matches: actual description: aStringOrBlock
>> > +
>> > +       self
>> > +               assert: [actual isString or: [actual isText]]
>> > +               description: [self
>> > +                       description: aStringOrBlock
>> > +                       with: (self comparingStringBetweenPattern: pattern andNonTextual: actual)].
>> > +       self
>> > +               assert: [self doesPattern: pattern match: actual]
>> > +               description: [self
>> > +                       description: aStringOrBlock
>> > +                       with: (self comparingStringBetweenPattern: pattern and: actual)].!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>comparingStringBetweenPattern:and: (in category 'private') -----
>> > + comparingStringBetweenPattern: pattern and: actual
>> > +
>> > +       ^ 'Expected pattern {1} does not match actual {2}.' translated
>> > +               format: {
>> > +                       pattern.
>> > +                       actual }!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>comparingStringBetweenPattern:andNonTextual: (in category 'private') -----
>> > + comparingStringBetweenPattern: pattern andNonTextual: actual
>> > +
>> > +       ^ 'Expected something that matches {1} but actual {2} is neither string nor text.' translated
>> > +               format: {
>> > +                       pattern.
>> > +                       actual }!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>comparingStringBetweenUnexpectedPattern:and: (in category 'private') -----
>> > + comparingStringBetweenUnexpectedPattern: pattern and: actual
>> > +
>> > +       ^ 'Unexpected pattern {1} does match actual {2}.' translated
>> > +               format: {
>> > +                       pattern.
>> > +                       actual }!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>comparingStringBetweenUnexpectedPattern:andNonTextual: (in category 'private') -----
>> > + comparingStringBetweenUnexpectedPattern: pattern andNonTextual: actual
>> > +
>> > +       ^ 'Expected something that does not match {1} but actual {2} is neither string nor text.' translated
>> > +               format: {
>> > +                       pattern.
>> > +                       actual }!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>deny:matches: (in category 'accessing') -----
>> > + deny: pattern matches: actual
>> > +
>> > +       ^ self
>> > +               deny: pattern
>> > +               matches: actual
>> > +               description: nil!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>deny:matches:description: (in category 'accessing') -----
>> > + deny: pattern matches: actual description: aStringOrBlock
>> > +
>> > +       self
>> > +               assert: [actual isString or: [actual isText]]
>> > +               description: [self
>> > +                       description: aStringOrBlock
>> > +                       with: (self comparingStringBetweenUnexpectedPattern: pattern andNonTextual: actual)].
>> > +       self
>> > +               deny: [self doesPattern: pattern match: actual]
>> > +               description: [self
>> > +                       description: aStringOrBlock
>> > +                       with: (self comparingStringBetweenUnexpectedPattern: pattern and: actual)].!
>> >
>> > Item was added:
>> > + ----- Method: TestCase>>doesPattern:match: (in category 'private') -----
>> > + doesPattern: pattern match: stringOrText
>> > +
>> > +       ^ (pattern respondsTo: #matches:)
>> > +               ifTrue: [pattern matches: stringOrText]
>> > +               ifFalse: [pattern match: stringOrText]!
>> >
>> >
>>
>>
>



More information about the Squeak-dev mailing list