www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - unit-threaded v0.7.45 - now with more fluency

reply Atila Neves <atila.neves gmail.com> writes:
For those not in the know, unit-threaded is an advanced testing 
library for D that runs tests in threads by default. It has a lot 
of features:

http://code.dlang.org/packages/unit-threaded

New:

* Bug fixes
* Better integration testing
* unitThreadedLight mode also runs tests in threads
* More DDoc documentation (peer pressure from Adam's site)
* Sorta kinda fluent-like asserts

On the new asserts (should and should.be are interchangeable):

     1.should == 1
     1.should.not == 2

     1.should.be in [1, 2, 3]
     4.should.not.be in [1, 2, 3]

More controversially (due to a lack of available operators to 
overload):

     // same as .shouldApproxEqual
     1.0.should ~ 1.0001;
     1.0.should.not ~ 2.0;

     // same as .shouldBeSameSetAs
     [1, 2, 3].should ~ [3, 2, 1];
     [1, 2, 3].should.not ~ [1, 2, 2];


I also considered adding `.should ~=`. I think it even reads 
better, but apparently some people don't. Let me know?

The operator overloads are completely optional.


Atila
May 05 2018
parent reply Johannes Loher <johannes.loher fg4f.de> writes:
On Saturday, 5 May 2018 at 13:28:41 UTC, Atila Neves wrote:
 For those not in the know, unit-threaded is an advanced testing 
 library for D that runs tests in threads by default. It has a 
 lot of features:

 http://code.dlang.org/packages/unit-threaded

 New:

 * Bug fixes
 * Better integration testing
 * unitThreadedLight mode also runs tests in threads
 * More DDoc documentation (peer pressure from Adam's site)
 * Sorta kinda fluent-like asserts

 On the new asserts (should and should.be are interchangeable):

     1.should == 1
     1.should.not == 2

     1.should.be in [1, 2, 3]
     4.should.not.be in [1, 2, 3]

 More controversially (due to a lack of available operators to 
 overload):

     // same as .shouldApproxEqual
     1.0.should ~ 1.0001;
     1.0.should.not ~ 2.0;

     // same as .shouldBeSameSetAs
     [1, 2, 3].should ~ [3, 2, 1];
     [1, 2, 3].should.not ~ [1, 2, 2];


 I also considered adding `.should ~=`. I think it even reads 
 better, but apparently some people don't. Let me know?

 The operator overloads are completely optional.


 Atila
Personally, I don't like that kind of "abuse" of operators at all. I think it looks really unusual and it kind of breaks your "flow" when reading the code. Additionally, people, who don't know about the special behaviour the operators have in this case, might get really confused. I would much prefer it, if you used a more common fluent style (like 1.0.should.be.approximately(1.0001);). Anyways, thanks for working on this awesome project!
May 05 2018
next sibling parent reply Dechcaudron <no-reply no-email.com> writes:
On Saturday, 5 May 2018 at 15:51:11 UTC, Johannes Loher wrote:
 Personally, I don't like that kind of "abuse" of operators at 
 all. I think it looks really unusual and it kind of breaks your 
 "flow" when reading the code. Additionally, people, who don't 
 know about the special behaviour the operators have in this 
 case, might get really confused. I would much prefer it, if you 
 used a more common fluent style (like 
 1.0.should.be.approximately(1.0001);).

 Anyways, thanks for working on this awesome project!
I think I'm siding with Johannes here. Much as the overloads look nice, I don't really see the advantage over `shouldEqual`. Also, what's with `all.these.identifiers`? Any particular reason why you are more fond of them rather than of good ol' pascalCase?
May 07 2018
parent reply Johannes Loher <johannes.loher fg4f.de> writes:
On Monday, 7 May 2018 at 09:19:31 UTC, Dechcaudron wrote:
 On Saturday, 5 May 2018 at 15:51:11 UTC, Johannes Loher wrote:
 Personally, I don't like that kind of "abuse" of operators at 
 all. I think it looks really unusual and it kind of breaks 
 your "flow" when reading the code. Additionally, people, who 
 don't know about the special behaviour the operators have in 
 this case, might get really confused. I would much prefer it, 
 if you used a more common fluent style (like 
 1.0.should.be.approximately(1.0001);).

 Anyways, thanks for working on this awesome project!
I think I'm siding with Johannes here. Much as the overloads look nice, I don't really see the advantage over `shouldEqual`. Also, what's with `all.these.identifiers`? Any particular reason why you are more fond of them rather than of good ol' pascalCase?
Fluent assertions have one major advantage over using pascalCase assertions: There is no ambiuguity about the order of arguments. When using e.g. assertEquals, how do you know wheter is is supposed to be assertEquals(actual, expected), or assertEquals(expected, actual)? The first one is the only one that makes sense wirh UFCS, but it is not clear directly from the API. On top of that, some popular Frameworks (I‘m looking at you, JUnit...) do it exactly the other way round. With fluent assertions, you don‘t have this Problem, it is much more clear that it should be actual.should.equal(expected) and not expected.should.equal(actual), because it fits naturally in the chain of ufcs calls.
May 07 2018
next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 05/07/2018 11:57 PM, Johannes Loher wrote:
 On Monday, 7 May 2018 at 09:19:31 UTC, Dechcaudron wrote:
 I think I'm siding with Johannes here. Much as the overloads look 
 nice, I don't really see the advantage over `shouldEqual`. Also, 
 what's with `all.these.identifiers`? Any particular reason why you are 
 more fond of them rather than of good ol' pascalCase?
Fluent assertions have one major advantage over using pascalCase assertions: There is no ambiuguity about the order of arguments. When using e.g. assertEquals, how do you know wheter is is supposed to be assertEquals(actual, expected), or assertEquals(expected, actual)? The first one is the only one that makes sense wirh UFCS, but it is not clear directly from the API. On top of that, some popular Frameworks (I‘m looking at you, JUnit...) do it exactly the other way round. With fluent assertions, you don‘t have this Problem, it is much more clear that it should be actual.should.equal(expected) and not expected.should.equal(actual), because it fits naturally in the chain of ufcs calls.
I don't think that's the issue. At least, it isn't for me. It's not a question of "assert equals" vs "should equal" (Though I am convinced by your argument on that matter). The question is: Why "should.equal" instead of "shouldEqual"? The dot only seems there to be cute. Not that I'm necessarily opposed to any of it (heck, I like cuteness in any sense of the word), it's just that: If the "~" thing is operator abuse, then I don't see how "should.equal", "should.not.be" etc, wouldn't fall into the same category.
May 08 2018
next sibling parent reply Cym13 <cpicard openmailbox.org> writes:
On Tuesday, 8 May 2018 at 07:07:30 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 05/07/2018 11:57 PM, Johannes Loher wrote:
 On Monday, 7 May 2018 at 09:19:31 UTC, Dechcaudron wrote:
 I think I'm siding with Johannes here. Much as the overloads 
 look nice, I don't really see the advantage over 
 `shouldEqual`. Also, what's with `all.these.identifiers`? Any 
 particular reason why you are more fond of them rather than 
 of good ol' pascalCase?
Fluent assertions have one major advantage over using pascalCase assertions: There is no ambiuguity about the order of arguments. When using e.g. assertEquals, how do you know wheter is is supposed to be assertEquals(actual, expected), or assertEquals(expected, actual)? The first one is the only one that makes sense wirh UFCS, but it is not clear directly from the API. On top of that, some popular Frameworks (I‘m looking at you, JUnit...) do it exactly the other way round. With fluent assertions, you don‘t have this Problem, it is much more clear that it should be actual.should.equal(expected) and not expected.should.equal(actual), because it fits naturally in the chain of ufcs calls.
I don't think that's the issue. At least, it isn't for me. It's not a question of "assert equals" vs "should equal" (Though I am convinced by your argument on that matter). The question is: Why "should.equal" instead of "shouldEqual"? The dot only seems there to be cute. Not that I'm necessarily opposed to any of it (heck, I like cuteness in any sense of the word), it's just that: If the "~" thing is operator abuse, then I don't see how "should.equal", "should.not.be" etc, wouldn't fall into the same category.
I wouldn't say it's an abuse, the dot means exactly the same thing as everywhere else in the language. I'm way less fan of overidding ~ since that doesn't have that meaning in any other context. Without having actually used it, I like the composability over pascalCasing here, it looks like it fits nicely in a functional environment with things like aliases and partials I think, defining your own primitives naturally... Nothing one can't do with regular functions and pascalCased assertions, but it sounds like it would be way more verbose. It also sounds like it's easier on the implementation side since you never have to define both a "shouldSomething" and "shouldNotSomething", and that means as a user I can expect less bugs and better maintainance of the library. That said, it'll have to be field-tested to be sure.
May 08 2018
parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 05/08/2018 05:05 AM, Cym13 wrote:
 
 I wouldn't say it's an abuse, the dot means exactly the same thing as 
 everywhere else in the language.
No, it really doesn't mean the same thing at all. Not when you look away from the unimportant implementation details and towards the big picture: Normally, saying "x.y" denotes composition and membership: It means "y, which is a member of x". Saying "x.y" does NOT normally denote "The boundary between word 'x' and word 'y' in an english-grammared phrase". But with things like "should.not.be", it's very much NOT a composition/membership relationship: A "be" is not really a member/property/component/etc of a "not", except in the sense that that's how the english-like DSL is internally implemented. A "should" is not really something that is composed of a "not", except in the sense that that's how the english-like DSL is internally implemented. (IF it even is implemented that way at all. I didn't look, so for all I know it might be opDispatch.) I'm not saying that "should.not.be" OR "~" are abuses, I'm just saying whether or not they are, they're definitely both in the same category: Either they're both abuses or neither one is, because they both do the same thing: utilize use existing syntax for something other than the syntax's usual semantic meaning. Formal "operator overloading" isn't the only way to alter (or arguably abuse) a language's normal semantics.
May 08 2018
parent reply Cym13 <cpicard openmailbox.org> writes:
On Wednesday, 9 May 2018 at 04:40:37 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 05/08/2018 05:05 AM, Cym13 wrote:
 [...]
No, it really doesn't mean the same thing at all. Not when you look away from the unimportant implementation details and towards the big picture: [...]
With UFCS I find that in my code a dot means "function composition" more often than "is a member of". Maybe it's just that I like writting in a functional style, but UFCS chains are very much endorsed by the language, so I wouldn't call it a stretch.
May 09 2018
parent bauss <jj_1337 live.dk> writes:
On Wednesday, 9 May 2018 at 10:37:52 UTC, Cym13 wrote:
 On Wednesday, 9 May 2018 at 04:40:37 UTC, Nick Sabalausky 
 (Abscissa) wrote:
 On 05/08/2018 05:05 AM, Cym13 wrote:
 [...]
No, it really doesn't mean the same thing at all. Not when you look away from the unimportant implementation details and towards the big picture: [...]
With UFCS I find that in my code a dot means "function composition" more often than "is a member of". Maybe it's just that I like writting in a functional style, but UFCS chains are very much endorsed by the language, so I wouldn't call it a stretch.
I agree with this in this case.
May 09 2018
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2018-05-08 09:07, Nick Sabalausky (Abscissa) wrote:

 The question is: Why "should.equal" instead of "shouldEqual"? The dot 
 only seems there to be cute.
It scales better. This way only one "should" function is needed and one "not" function. Otherwise there would be a lot of duplication, i.e. "shouldEqual" and "shouldNotEqual". Hopefully the library can have a one generic implementation of "not", where it doesn't if the assertion is "equal", "be" or some other assertion. -- /Jacob Carlborg
May 09 2018
prev sibling parent Dechcaudron <no-reply no-email.com> writes:
On Tuesday, 8 May 2018 at 03:57:25 UTC, Johannes Loher wrote:
 Fluent assertions have one major advantage over using 
 pascalCase assertions: There is no ambiuguity about the order 
 of arguments.

 When using e.g. assertEquals, how do you know wheter is is 
 supposed to be assertEquals(actual, expected), or 
 assertEquals(expected, actual)? The first one is the only one 
 that makes sense wirh UFCS, but it is not clear directly from 
 the API. On top of that, some popular Frameworks (I‘m looking 
 at you, JUnit...) do it exactly the other
 way round.

 With fluent assertions, you don‘t have this Problem, it is much 
 more clear that it should be actual.should.equal(expected) and 
 not expected.should.equal(actual), because it fits naturally in 
 the chain of ufcs calls.
Okay, I think I see your point, although it looks to me the added verbosity and code complexity is not really worth it, provided you always use UFCS. But of course, that cannot be easily enforced I guess.
May 08 2018
prev sibling parent bauss <jj_1337 live.dk> writes:
On Saturday, 5 May 2018 at 15:51:11 UTC, Johannes Loher wrote:
 On Saturday, 5 May 2018 at 13:28:41 UTC, Atila Neves wrote:
 For those not in the know, unit-threaded is an advanced 
 testing library for D that runs tests in threads by default. 
 It has a lot of features:

 http://code.dlang.org/packages/unit-threaded

 New:

 * Bug fixes
 * Better integration testing
 * unitThreadedLight mode also runs tests in threads
 * More DDoc documentation (peer pressure from Adam's site)
 * Sorta kinda fluent-like asserts

 On the new asserts (should and should.be are interchangeable):

     1.should == 1
     1.should.not == 2

     1.should.be in [1, 2, 3]
     4.should.not.be in [1, 2, 3]

 More controversially (due to a lack of available operators to 
 overload):

     // same as .shouldApproxEqual
     1.0.should ~ 1.0001;
     1.0.should.not ~ 2.0;

     // same as .shouldBeSameSetAs
     [1, 2, 3].should ~ [3, 2, 1];
     [1, 2, 3].should.not ~ [1, 2, 2];


 I also considered adding `.should ~=`. I think it even reads 
 better, but apparently some people don't. Let me know?

 The operator overloads are completely optional.


 Atila
Personally, I don't like that kind of "abuse" of operators at all. I think it looks really unusual and it kind of breaks your "flow" when reading the code.
I agree with this. If the comments weren't added, nobody reading the code would have any idea what it actually does except for whoever wrote it.
May 09 2018