DaedTech

Stories about Software

By

ChessTDD 24: Cleaning Up for Acceptance Testing

It’s been a little while since I posted to this series, largely because of the holiday time.  I’ve wrapped up some things that I’d been working on and am hoping here in the next few weeks to have some time to knock out several episodes of this, so look for an elevated cadence (fingers crossed).  To get back into the swing of things in this episode, I’m going to pull back a bit from the acceptance test writing and clean up some residual cruft so that when I resume writing the acceptance tests in earnest, I’m reacquainted with the code base and working with cleaner code.

Here’s what I accomplish in this clip:

  • Refactored awkward test setup to push board population to production code.
  • Got rid of casting in acceptance tests (and thus prepared for a better implementation as we start back up).

Here are some lessons to take away:

  • Refactoring is an interesting exercise in the TDD world when you’re moving common functionality from tests to production.  Others’ mileage may vary, but I consider this to be a refactoring, so even moving this from test code to production code, I try to stay green as I go.
  • I made a mistake in moving on during a refactoring when my dots went dark green.  Turns out they were green for the project I was in even while another project and thus the solution were not compiling.  It’s good to be mindful of gotchas like this so that you’re not refactoring, thinking that everything is fine, when in reality you’re not building/passing.  This is exactly the problem with non-TDD development — you’re throwing a lot of code around without verification that what you’re doing isn’t creating problems.
  • It’s not worth agonizing over the perfect implementation.  If what you’re doing is better than what existed before, you’re adding value.
  • If you’re working experimentally and trying things out and your tests stay green for a while when you’re doing this, make sure you can get to red.  Take a second and break something as a sanity check.  I can’t tell you how frustrating it is to be working for a while and assume that everything’s good, only to learn that you’d somehow, inadvertently made the tests necessarily always pass for some trivial reason.

By

Chess TDD 23: Yak-Shaving with SpecFlow

I’m writing out this paragraph describing what I’m planning to do before recording myself doing anything. This post has been a while in coming because I’ve waffled between whether I should just hand write acceptance/end-to-end tests (what I generally do) or whether I should force you to watch me fumble with a new tool. I’ve opted for the latter, though, if it gets counter-productive, I may scrap that route and go back to what I was doing. But, hopefully this goes well.

Having fleshed out a good bit of the domain model for my Chess move calculator implementation, the plan is to start throwing some actual chess scenarios at it in the form of acceptance tests. The purpose of doing this now is two-fold: (1) drive out additional issues that I may not be hitting with my comparably straightforward unit test cases and (2) start proving in English that this thing works. So, here goes.

Here’s what I accomplish in this clip:

  • Installed SpecFlow plugin to Visual Studio
  • Set SpecFlow up for the ChessTDD project.
  • Wrote a simple acceptance test.

Here are some lessons to take away:

  • There is a camp that favor ATDD (acceptance test driven development) or “outside-in TDD” and another that prefers “traditional” or “inside-out TDD.”  I’m not really in either, but you might find that you have a preference.  Both approaches are worth understanding.
  • What I’m doing right now, for the moment, is not test driven development.  I’m retrofitting acceptance tests to bolster my coverage and see if the more micro-work I’ve done stands up to real world usage.
  • If you want to see how someone that doesn’t know SpecFlow gets it set up, this is the video for you.
  • If you’ve been tinkering with a test for a while and the test was green the whole time, how do you know you’re done?  With your assert in place as you think it should look, modify it quickly to something that should fail and confirm that it does.  The idea here is to make sure that your modifications to the test have actually had an effect.

By

Chess TDD 22: Friendly Pieces

This episode saw me make the first distinction between friendly and enemy pieces in determining available moves.  It had also been a while since I’d worked on the series, so I was a little uncomfortable with how well I was reasoning at compile time about what was happening.  The Board class and some of my implementations are getting a little awkward and I’d like to spiffy them up.  However, I think that I’m going to start focusing on writing acceptance tests right now to bolster the correctness of what’s going on.  This will allow me to separate fixing any flaws in my reasoning from making the code more readable, which really are two separate things.

Here’s what I accomplish in this clip:

  • Stopped knight from being able to land on friendly pieces
  • Implemented the concept of capture
  • Stopped allowing jump of any pieces for the purpose of capture

Here are some lessons to take away:

  • Sometimes you think it’s going to be a lot harder than it is to get everything green.  I was expecting a lot of red when I added the restriction that the move target couldn’t contain a piece, but none happened.  Better to know quickly via experiment than spend a lot of time squinting at your code.  It’s important to reason out why you were mistaken, but getting verification first and working backward will generally save time.
  • You can get a little too clever with what you’re doing.  I was trying to get to green by adding sort of a silly hard-coding, and it came back to bite me.  Such is the nuance of “do the simplest thing to go green.”  No matter how many times you do this, there will always be times you fumble or do dumb things.
  • I got a bit sidetracked trying to figure out how to push that base class constructor into the children, but came up empty.  I’m not going to lie — if I weren’t recording for an audience, I would probably have scoured the internet for a way to do that.  If you’re not in a time crunch or you’re willing to do that on your own dime, these can be great learning exercises.
  • As I work with these Linq expressions, you’ll note that I’m not especially concerned about whether I’m iterating over a collection too many times or performance in general.  Not that it’s directly applicable, per se, but I’ve always loved this post from Jeff Atwood.  There are only two things that make me care about performance in the slightest: unhappy customers and unhappy me with bogged down unit tests.  Until one of those things starts happening, I consider performance orders of magnitude less important than readability and just about any other concern you can think of.  I can easily make readable code faster, if necessary.  It’s really hard to make a pile of unreadable crap written “for performance purposes” into something that makes sense.
  • We’re getting fairly close to a full implementation of non-specialty moves (en passant, last row replacement, castling), so perhaps its time to take a real chess game and model the actual, possible moves to create an acceptance test suite.  Stay tuned.

By

ChessTDD 21: False Starts and The Knight Problem

This wasn’t the most productive episode I’ve ever recorded, per se, in terms of functionality, but, hey, it’s always a learning experience to write code.  This was kind of table setting for a big, important piece of functionality: distinguishing captures from blocked moves because of friendly pieces.

Here’s what I accomplish in this clip:

  • Flesh out the non-happy path scenario for PathChecker (later renamed PathMaker)
  • Start implementing functionality for Knight on the board.

And, here are some lessons to take away:

  • If you write a test that you think should pass and it fails, it’s okay to pivot what you’re testing to gain understanding.  Improving your reasoning about the code is extremely important.
  • Thrashing can happen.  It occurred to me that GetSpacesAlongPath maybe shouldn’t return the destination piece in its collection, but that created problems for existing code.  I weighed the dependencies and what would need to change the functionality and decided clarifying the name was a better route.
  • It’s okay to write and leave tests to confirm your hypotheses.  The TDD discipline is to force yourself to change production code only in response to a failing unit test, but there’s nothing to say you can’t write green tests to confirm that the system is working as you expect.  I have no qualms about weaving this sort of thing into red-green-refactor provided that the test is one that is meaningful when left in place because it’s important to corroborate your reasoning about the code.
  • Don’t call it a day with a failing unit test.  If you’re not prepared to finish by making it green, back it out and add a todo list item.  You don’t want to start at red next time.

By

ChessTDD 20: Refactoring in Earnest

In this post, I set out to do some real refactoring of the Board class.  It bothered me enough to take a crack at it and, since this is a fun side project, there really aren’t any constraints.  Were I committed to delivering some business value here, I might need to take a look at my priorities and evaluate whether making things clearer here is worth the delay.  But, luckily in this case, I don’t need to make that call.  And, refactorings are always fun.

Here is what I accomplished in this clip:

  • Refactored a couple of methods out of Board and onto BoardCoordinate.
  • Refactored path checking logic into a PathChecker class.

Here are lessons to take away:

  • If you have functionality that’s purely static in some class (as in, not referring to instance variables in that class), think about where else it might go.  If that static method is principally interested in operating on properties or with methods of another type, you might have the “feature envy” code smell.  This was the case with my static methods that evaluated whether two BoardCoordinates were on the same horizontal or vertical path.  They compared properties on two different BoardCoordinates — so why not make this a member of BoardCoordinate?
  • This isn’t really something I’ve been emphasizing here, but early on I decided to do a quick local commit with Git.  Commit early and often.  I’ve never regretted too many commits unless I was using a terrible source control tool.
  • Defining  a class in the same space as another class is a tool that can help me with extract class refactorings.  This is one of the more volatile refactorings that you can do, so make sure you don’t try to do too much at one and that you get green regularly as you go.  Recreate the functionality faithfully first and then think about how to refactor.
  • If you can factor a method toward not referring to any instance state, then you’re well on the way to letting it be moved to a different class.  This is a good intermediate step to reason about — see how much instance state you can abstract out.
  • When you extract a class as part of a refactoring, it’s fine to leave the tests that cover both classes in place.  In fact, it’s a natural set of tests to leave.  Add tests for the newly created class only to address new complexities that arise from the newly exposed functionality needing to tighten up guard conditions and such.