<div dir="ltr">Wow! Thanks for an excellent reply! I really appreciate it...<br><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Tue, Dec 24, 2013 at 11:38 AM, bearophile <span dir="ltr"><<a href="mailto:bearophileHUGS@lycos.com" target="_blank">bearophileHUGS@lycos.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">John Carter:<div><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
I also attempted to do it in "Best Practice Test Driven Development" form<br>
</blockquote>
<br></div>
I think it's important to write lot of unittests (and use other kind of tests, automatic testing like in Haskell, fuzzy testing, integration tests, smoke tests, etc), but I think Test Driven Development is mostly silly. It's better to actually think a good solution before writing your code. Thinking-Driven Development is better.<br>
<div></div></blockquote><div><br></div><div>Part of the aim of the exercise was to explore that difference.<br><br></div><div>Conclusions from that exploration?<br><br></div><div>1) Very fine grained steps resulted in fewer bugs and fewer backtracks.<br>
</div><div>2) Very fine grained steps resulted in better factored code.<br></div><div>3) Watching others do the same exercise led me to the attitude "adding more test cases is Bad". <br></div><div>4) Very fine grained steps slowed me down compared to my more usual somewhat loose style.<br>
</div><div>5) Thoughtfully adding Provocative test cases in the order of "What I need to Handle Next" is Very Very Good.<br></div><div>6) Replacing "Fine Grained Steps" with, whenever stuck, pull out a smaller chunk and TDD that first (with Provocative test cases) worked Very Very Well.<br>
<br></div><div>Bottom Line?<br><br></div><div>In future I will be doing...<br></div><div>* Thoughtfully choosing a minimal number of provocative test cases. (Ones that direct me to growing the code correctly)<br></div><div>
* I will take the largest steps I can where I can go from test fail to test pass in a single change.<br></div><div>* Whenever I misjudged and take too large a step, I will factor out a smaller function and test and develop that first via the same process.<br>
</div><div> </div><br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
assert( i != 0);<br>
</blockquote>
<br>
Sometimes you can also add a comment in the assert that explains why this precondition has failed</blockquote><div><br></div><div>In the Ruby world the test frameworks (plus some of my own utility modules) give a very rich set of assertions and logging information on assert failures. I usually can just look at the output of an Ruby assert failure in my code and tell you exactly what the bug is.<br>
<br></div><div>For this case I resolve not to get into that, but ultimately I would love to find a D module that will give that rich amount of info. Any recommendations?<br><br><blockquote style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex" class="gmail_quote">
have not studied the logic of this code, but are you sure toUFT8 is
needed to convert Arabic numbers to Roman and the other way around?<br></blockquote>
<br></div><div>I'm storing the roman numerals as dchars. I have the vague hope this is more efficient than storing them as strings. My reasoning has more to do with "That's the way I did it with ASCII and C strings and chars" than profiling or deep knowledge.<br>
<br></div><div>Earlier versions I uses strings, but when I want to iterate of a string in fromRoman I converted to dchars.<br></div><div><br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
return toUTF8([digit]) ~ toRomansWithZero( i - value);<br>
</blockquote>
<br>
More missing UCSF.<br></blockquote><div><br></div><div>Alas, my google search results are cloudy by millions of hits for University of California San Francisco. (Wow! Google search is getting fast these days... Already getting a hit on your Post!)<br>
<br></div><div>What do you mean by "missing UCSF"?<br></div><div> <br clear="all"></div></div><blockquote style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex" class="gmail_quote">
do you have negative tests that tests the code raises an assertion Error
when you give a 0 to the function?<br></blockquote><br></div><div class="gmail_extra">Interestingly enough I did.<br><br></div><div class="gmail_extra">In the somewhat ugly form of...<br>unittest {<br> try {<br> toRoman( 0);<br>
assert( false);<br> } catch( core.exception.AssertError assertError) {<br> assert(true);<br> }<br>}<br><br></div><div class="gmail_extra">But when I added the @safe dmd said...<br>roman.d(314): Error: can only catch class objects derived from Exception in @safe code, not 'core.exception.AssertError'<br>
<br></div><div class="gmail_extra">I figured @safe was worth more to me than that test so I discarded it.<br><br></div><div class="gmail_extra">I would love away around that.<br><br></div><div class="gmail_extra">In Ruby I tend to have several flavours of AssertError, for example PreconditionFailure, PostConditionFailure and InvaraintFailure.<br>
<br><blockquote style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex" class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
return array(map!((a) => romanToDigit(a))(roman));<br>
</blockquote>
<br>
That's quite bad code. Better:<br>
<br>
return roman.map!romanToDigit.array;<br>
<br></blockquote></div><div class="gmail_extra"><br></div><div class="gmail_extra">That certainly looks nicer, alas, dmd is unhappy....<br>dmd -unittest -main roman.d && ./roman<br>/usr/include/dmd/phobos/std/algorithm.d(425): Error: function roman.romanToDigit is not accessible from module algorithm<br>
/usr/include/dmd/phobos/std/algorithm.d(459): Error: function roman.romanToDigit is not accessible from module algorithm<br>/usr/include/dmd/phobos/std/algorithm.d(411): Error: template instance std.algorithm.MapResult!(romanToDigit, string) error instantiating<br>
roman.d(232): instantiated from here: map!string<br>roman.d(232): Error: template instance std.algorithm.map!(romanToDigit).map!string error instantiating<br><br></div><div class="gmail_extra">As a halfway point I have taken it to...<br>
/// Convert string of char code points to array of UTF32 so we can have random access.<br>pure immutable uint[] romanToDigitArray( in string roman)<br>{<br> return roman.map!((a) => romanToDigit(a)).array;<br>}<br><br>
<div class=""><span name="Xinok" class="">Xinok</span> <span class=""><<a href="mailto:xinok@live.com">xinok@live.com</a>></span>said...</div><br></div><div class="gmail_extra"><blockquote style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex" class="gmail_quote">
There are a series of unittests which could be merged together. Keep the
line comments to keep different tests separate, but there's no need to
have multiple unittests which contain nothing more than a few simple
asserts. Or as bearophile suggested, use an in-out table<br></blockquote><div><br></div><div>Actually I do have a reason for keeping them separate.<br><br></div><div>I have been taking on board what books like "xUnit Test Patterns by Gerard Meszaros" and various videos by JB Rainsberger say.<br>
<br></div><div>I used to have long "Ramble On" unit tests, but found from painful experience that the above authors are dead right. <br><br>Ramble on tests are fragile, hard to maintain and destroy defect localization.<br>
<br></div><div>One of the prime values of unittesting is unlike an integrated test, a unit test failure in a well designed suite of unit tests, tells you exactly where the defect is.<br><br></div><div>Rainsberger talks about having "Only one reason for a test to fail, only one test to fail if you have a defect". I'm not quite sure how to achieve that.<br>
<br></div><div>My main gripe with my current set of tests is I have too many. Each test should be pinning exactly one behaviour of the problem, and I should have only one test for each behaviour.<br><br></div><div>In terms of having them data driven, it comes back to what I was saying about needing a richer palette of asserts and better feed back from an assert failure.<br>
<br></div><div>If I make them data driven I will lose which test case failed.<br><br></div><div>Ideally I should make them data driven AND use a more informative assert facility that tells me what the expression was and what the value of the variables that went in to that expression were.<br>
<br><blockquote style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex" class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
// Test the 'I''s<br>
unittest {<br>
</blockquote>
<br>
Some unittest system allows to put that comment "Test the 'I''s" in a
more active role. So it's shown on screen when the relative unittest
fails.<br>
<br>
Also, I suggest to add some "ddoc unittests". It's a feature recently added to D.<br></blockquote>
<br></div><div>I don't quite understand this comment...<br><br></div><div>I thought it meant just putting a ddoc /// extra / flag on the comment, but nothing new happened either on a test failure or on "dmd -D" output (version v2.064)<br>
</div><div><br></div><div>Revised version with most comments worked in now up at...<br><a href="https://bitbucket.org/JohnCarter/roman/src/0362f3626c6490490136b097f23c67fc2970c214/roman.d?at=default">https://bitbucket.org/JohnCarter/roman/src/0362f3626c6490490136b097f23c67fc2970c214/roman.d?at=default</a><br>
<br></div><div>Once again, thanks to everyone for fantastic feedback.<br></div><div><br></div>-- <br><div dir="ltr">John Carter<br>Phone : (64)(3) 358 6639<br>Tait Electronics <br>PO Box 1645 Christchurch<br>
New Zealand<br>
<br></div>
</div></div>
<br>
<hr><font color="#808080">This email, including any attachments, is only
for the intended recipient. It is subject to copyright, is confidential
and may be the subject of legal or other privilege, none of which is
waived or lost by reason of this transmission.</font><div><font color="#808080">If
you are not an intended recipient, you may not use, disseminate,
distribute or reproduce such email, any attachments, or any part
thereof. If you have received a message in error, please notify the
sender immediately and erase all copies of the message and any
attachments.</font></div><div><font color="#808080">Unfortunately, we
cannot warrant that the email has not been altered or corrupted during
transmission nor can we guarantee that any email or any attachments are
free from computer viruses or other conditions which may damage or
interfere with recipient data, hardware or software. The recipient
relies upon its own procedures and assumes all risk of use and of
opening any attachments.</font></div><div><hr></div>