Code from Software Engineering 101 TDD session + more practice
Thanks to everyone who tuned in to watch our live TDD session. Here is the completed code that we ended up with. If you want to try it for yourself, you can get the rules for the Greed game here.
Here are some other “kata” exercises that you can use to help with your TDD practice. These are other simple examples similar to the Greed game.
String Calculator
Bowling scoring
Tennis scoring
Happy testing!
Thanks for suggesting Greed scoring as a coding task. I tried to apply TDD as I did this, but at some point I think my adherence to that discipline broke down and I reverted to standard REPL refinement.
As has typically been my experience, it is not clear to me how much benefit came from the TDD practice, per se. My final code did, however, emerge from something I think would qualify as refactoring.
scoregreed =: (triple + singles) @: tallyByFace
tallyByFace =: [: (+/"_) 1 2 3 4 5 6 (="_ 0) ]
singles =: [: +/ 100 0 0 0 50 0 * ( ] - 3 * 3 <: ] )
triple =: [: +/ 1000 500 500 500 500 500 * ( 3 <: ] )
I’m only posting my solution code, not any test code. For those who do not recognize it, this is written in the J programming language.
In addition to the problem of WordPress changing one character in the code I posted, I found the more important problem of my code not meeting the specs. I corrected the code to meet the published requirements, and post that below.
scoregreed =: (triple + singles) @: tallyByFace
tallyByFace =: [: (+/"_) 1 2 3 4 5 6 (="_ 0) ]
singles =: [: +/ 100 0 0 0 50 0 * ] - 3 * 3 <: ]
triple =: [: +/ 1000 200 300 400 500 600 * 3 <: ]
The solution Jon Kruger posted is not only more than ten times longer, it strikes me as a lot less amenable to the changes that might happen if we wanted to take this as a starting point for a somewhat different, but largely similar, rule set.
As for my noteworthy failure of not matching the requirements, and thus posting incorrect code, I don’t know what aspect of TDD might have helped me avoid that. It might be that if I’d held more strictly to TDD longer than I did, rather than use it only in the early phases of this effort, I would have caught that deficiency. But if so, why?
@Tracy,
I’m not familiar with the J programming language, so I can’t comment on it. I’m sure I would understand what you’re doing better if I knew the language (and if WordPress didn’t decide to mangle your code).
Personally I don’t think fewer lines of code is necessarily a good metric. I certainly try to eliminate duplication, but I value readability and maintainability a lot because people are constantly having to change code. Sometimes this leads to more lines of code than a more succinct solution, but if it helps me remember what I did a year later, then I’m happy with it.
In .NET, writing tests first can help us design our code. In J, I’m not sure if it works the same way. But if you had written all of the tests up front and then done the implementation code, at least you would’ve known right away if it was working or not.
I hope you didn’t take all of this personally, I wasn’t trying to bash your code (like I said, I have no idea what good J code is anyway). I just think that readability and having tests is really important.
Jon,
I share your sense that readability and tests are very important. I’ve been putting my attention toward fitting TDD with J, and it continues to be tricky. I’ve not had the experience you’ve described where writing tests first helps in the design process. I have, however, been finding value in applying what I understand to be the central TDD ideas. Don’t worry; I didn’t take any of your comments personally.
I think it’s worth emphasizing that this J solution involves defining only four functions, and that the main function describes the overall program in what I think is a clear, simple manner. To put it in English, scoregreed first tallies how many of each die-face (number) have been rolled, then uses that as input to the functions that return the score of a triple and the score of rolled singles. Those scores are added together. That’s the main program.
At the top level it reads rather like pseudocode, except there’s nothing pseudo about it; it’s just code.
With regard to testing, I did rely on building and running automated tests. The mistake I made came from relying on tests and focusing only on examples. What I failed to do was look at the rules algebraically. I think it’s safe to say that tests are weaker than algebra, and cannot replace it. At the same time, I think there’s serious value that comes through tests. I’m striving for greater skill in finding the appropriate balance between these aspects of programming.
Let me close by explaining where the code got mangled. The token that is intended to occur between 3 and ] at the end of two functions is spelled less-than colon. It is the J symbol for less-than-or-equal-to. In this case, it tests whether 3 is less than or equal to the function’s input. That input is a list telling how many of each possible die face we’re dealing with, so for any face if there were three or more we have a triple.