DaedTech

Stories about Software

By

Undercover Testability Killers

Editorial Notes: I originally wrote this post for the Infragistics blog.  You can check out the original here, at their site.  While you’re there, take a look around at the other posts by their contributing authors.

If you were to take a poll of software development shops and ask whether or not they unit tested, you’d get varied responses.  Some would heartily say that they are, and some would sheepishly say that they totally mean to get around to that next year and that they’ve totally been looking into it.  In the middle, you’d get a whole lot of responses that amounted to, “it’s complicated.”

In my travels as a consultant, I witness the reason for this firsthand.  The adoption rate of automated testing has increased dramatically in the last decade, and that increased adoption means that a lot of shops are taking the plunge.  And naturally, this means that a lot of shops with a lot of legacy code and awkward constructs in their codebases are taking the plunge, which leads to interesting, complicated results.

Complicated

It’s Complicated

“It’s complicated” generally involves variants of “we tried but it wasn’t for us” and “we do it when we can, but the switch hasn’t flipped yet.”  And, at the root of all of these variants lies a truth that’s difficult to own up to when talking about your group – “we’re having trouble getting any good at this.”

If this describes you or folks you know, take heart, though.  The “Intro to TDD” and “NUnit 101” guides make it look really, really easy.  But those sources of learning usually show you how to write unit tests for things like “in-memory calculator,” intending to simplify the domain and code so that you understand the mechanics of a unit test.  But, in doing this, they paint a deceptive picture of how easy covering your code with tests should be.

If you’ve been writing code for years with nary a thought to testing at the unit level, it’s likely that familiar, comfortable coding practices of yours are proving to be false friends.  In other words, your codebase is probably littered with things that are actively making your life extremely difficult as you try to adopt automated testing.  What follows are some of the most common ones that I see.

Read More

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

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

Chess TDD 19: DoesPieceExistAt and Housekeeping

Another night in the hotel, another episode of ChessTDD.  I’m doing my day to day work mainly in Java-land these days, so this is about the only time I write .NET code anymore.  Anywho, in this episode, I’m just planning to pick some housekeeping cards and move them across, starting with one I created after receiving an astute observation from Jeff in the comments from last episode.

Here’s what I accomplished in this clip:

  • Fixed a subtle bit of weirdness that may have caused future problems.
  • Cleaned up the screwy logic around default board size.
  • Factored the test classes more toward the new naming paradigm.
  • Implemented a DoesPieceExistAt() method to get away from GetPiece() == null

Here are lessons to take away:

  • This is kind of a combined lesson from last time to this time.  The comment I received and my resulting attention to the issue he pointed out is an excellent example of why pair programming (or at least code review) is full of win.  I may or may not have caught that logic error before shipping this thing to (an imaginary) QA or production, but with that review, it’s at the fore and I can do something about it.
  • “Boy Scouting” isn’t just about improving production code.  It applies to anything, including your tests.  Wholesale, quixotic refactoring efforts are not necessary if you’re constantly improving the code a little bit in the areas that you’re touching anyway.  In other words, you needn’t always go out of your way to refactor — it’s often advisable to wait to touch production code until you’ll be in there anyway.
  • I like to default to immutability whenever possible.  In the case of board size, why make that a settable property?  How often does it come up that you want to change the size of a chessboard that already exists with scissors or tape and cardboard?  (Modeling the physical world just makes for a funny mnemonic — faithfully modeling it isn’t actually that important.)  Seriously, what advantage is there to that being mutable. In my book, none.  You want a different board size, make a new board.  Not having board size be mutable makes your life much easier — no reasoning about what happens if you shrink the board when pieces are on it and other things like that.
  • Class level preconditions passed into a constructor are important to guard against.  Don’t let your consumers pass you things that would render you in a nonsensical state, such as accepting negative 1 as a board size.  When this happens, fail quickly, loudly, and visibly with an exception, and make it clear why and how in your tests.