Welcome back for the third installment in my series (see Part 1, Part 2) wherein I chronicle my efforts refactoring some code I wrote over four years ago to implement a chess rules engine and WinForms control using C# under .NET v1.1. I’m hoping to demonstrate over the next few posts a few tips, techniques and tricks to improve established code with refactorings, unit tests and application of best practices that you might find useful in your own endeavours.
Last week I mentioned two tools that I’ll be using from this point forward to support constructing and running unit tests so we can backstop our new code against making destructive changes that would otherwise bust the build or introduce new bugs that we didn’t intend: NUnit and TestDriven.NET. For the impatient out there, we will be getting into refactoring soon, but as we’ll see below, I ran into a little issue as a result of setting up my tests that necessitated some backtracking.
Briefly, here’s a quick overview of each tool:
In spite of rumours on its demise, NUnit is very much the de facto if not the de jure free (as in beer) unit test suite for the .NET Framework. Yes, Visual Studio 2005 in the Team Editions supports its own unit test framework, which looks a lot like NUnit tests with differently-named method and class decorators, but I prefer the original. NUnit is modeled after JUnit, the Java unit test suite which in turn was based on seedwork by Kent Beck in Smalltalk – it has, therefore, a pretty solid pedigree.
NUnit provides you with a framework for writing regression test assertions to validate code behaviours along with both a console and GUI test runner harness. If you haven’t done any Test Driven Development (TDD) before, this will be a pleasant experience as you won’t have to roll-your-own test harnesses to see if your code works – you’ll just run the test assemblies and get immediate feedback on whether they passed or failed.
Another upshot to using a framework like NUnit is the option of later “hooking” your tests into a continuous integration suite like CruiseControl.NET which can synthesize your code versioning, build and tests to run automatically on code check-ins or other triggering events.
I’ve installed the latest stable version of NUnit for this exercise: 2.4.3 for .NET 2.0.
While NUnit is the cat’s pajamas for unit testing, and does support some IDE integration, it lacks a certain panache since its test-running goodness is run via an external app. This can be a throw-the-hands-in-the-air point for some folks who just cannot live without doing everything in Visual Studio, and will forever despise writing and running unit tests without some IDE support. So, to kick things up a notch and keep these folks interested, on-board and insanely productive, I recommend downloading and installing the TestDriven.NET Visual Studio add-in.
I won’t go much further into the addin except to say that it allows you the ability to run your tests simply by right-clicking inside either the test fixture class or test method – easy peasy. If you want to know more, check out the developer’s site for excellent overviews and tutorials. Ok, here’s a quick screen cap, just to whet the appetite:
I’m using the latest stable version: 2.8.2130 – It runs in all versions of Visual Studio except the Express Editions, which is for a whole host of reasons.
Getting ready to test: Adding a test fixture project
Once NUnit is installed, all of its core libraries are available to all and sundry projects through the GAC, so all we need to do is create a new library for housing our test classes (aka Test Fixtures) and add a reference to the NUnit.Framework to get access to the all the unit testing goodness. There are a number of schools of thought on how tests could/should be organized and named – below is my preference:
In this way, I create an NUnit test assembly for each assembly or executable I’m testing and it is clearly identified with the .Tests suffix. Now, I’ll add references for the RNChessRulesEngine and RNChessBoardCommonTypes projects, since there are dependencies between them; next, I’ll add a reference to the nunit.framework assembly via the .NET tab in the Add Reference window. Finally, I’ll rename the stubbed-out class ChessRulesEngine.TestFixture.cs, add some namespace references and an NUnit TestFixture attribute to indicate that this class will be used for containing test code:
Now that I’m this far, I’ll do a quick build and commit the new project to source control before proceeding.
Supporting the rules engine with preliminary unit tests
In the last installment, I decided to start my refactoring within the DoPlayerMove(int32,int32) method in the rules engine as it had not only a high cyclomatic complexity score, but was also an obvious entry point for running subsequent routines. This brings up an some questions: How do we know if the engine is running correctly right now? Subsequently, what kind of tests should we be writing?
Note: If you’ve never written a unit test before, don’t panic: It’s dead easy. Check out the NUnit Quickstart tutorial for some examples.
At a fundamental level, we want to craft tests that will demonstrate that the basic operations of the engine are sound, ie. that we trap for bad input, process moves correctly and can play out a game to mate. My first inclination is to code up a test that would play a well-known, classic game like Capablanca vs. Alekhine 1927 – French Exchange. This way, I know for certain what the outcomes after each move should be, the material captured, number of half moves (white) and full moves (black) that have occurred, check states for either side’s king and checkmate state.
Algebra rears its ugly head. Well, sort of.
Sounds like we have a plan, right? Of course, there’s always a little trip-up: DoPlayerMove() is a private method that is called by another private method, CommitPlayerMove(), which in turn is called by the publicly-accessible MakeMove(). We have our work cut out for us: In order to run a sample game through the engine, we need to validate the behaviours for MakeMove() which takes input from two strings representing the Standard Algebraic Notation (SAN) for the “from” and “to” squares, using the numbers 1–8 and letters A-H to represent the ranks and files (rows and columns of squares)respectively. Thus, if we want to make a basic Queen’s pawn opening, we’d want to move from “D2” to “D4”.
Let’s build-out a test to verify that the input “D2” and “D4” results in a valid move. First, we need to add a member object for the rules engine so we can test it, and a special NUnit method to instantiate the object when the battery of tests are run for the first time called TFSetUp():
Note the [TestFixtureSetUp] attribute that tells the NUnit framework to run this method exactly once for every run of our test fixture class. Next, let’s add a method to exercise MakeMove() with the SAN I mentioned above:
A few things to note here:
- The [Test] attribute, which indicates that this method is a unit test and should be executed by the NUnit framework;
- The test method name follows a convention to make it easy to identify (hat tip to Roy Osherove) the method, test state and expected outcome for the test.
- I’m using a classic NUnit assertion, Assert.AreEqual(), which compares an expected value (MoveResult.Legal enum) against the actual value returned from the PlayerMoveStatus property, which is a struct defined in RNChessDataTypes.cs. I’ve also added a descriptive message that will be returned when the test fails so that we can get meaningful feedback.
Observant keener types will notice that in my Assertion, I’m expecting MoveResult.ILLEGAL. This is because I want the test to fail first – this way I can be assured that the method is running correctly. Right-click inside the test method and select Run Test(s) from the context menu – you’ll see the following in the Output pane:
Blammo! We have our first validation point that the engine is working correctly – it’s taking input for a valid move and is returning a confirmation. Moving on, we adjust the method to check for PlayerMoveResult.LEGAL and the we get a pass:
Let’s add another test to check out an illegal opening move – say the King’s pawn, three squares forward. We’ll follow the same pattern as above:
And the same drill as before: Run the test, watch it fail, then adjust to assert on MoveResult.ILLEGAL; run the test and it should pass easily.
Note that I’ve re-instantiated ChessRulesEngine on line 41 – I wanted to revert the object to its default state so that I could properly test the opening move. However, why not use the object’s ResetBoard() or UndoLastMove() methods to do the same thing? Because we want to make sure that our tests can be run in isolation of each other and test only the operations we’re controlling. By calling in ResetBoard() or UndoLastMove() inside a test method, we’re assuming that they work properly – until we know otherwise, we need to do like Fox Mulder and trust no-one!
So, I need to refactor my test code. I want to reset the state of the ChessRulesEngine object before each test without having to new one up. We can do this via a method decorated with the SetUp attribute:
We can take out line 41 in the MakeMove_OpenE2E5_Illegal() test method, build and run the tests for sanity and revel in our Homer Simpson-style productivity gains. Don’t forget, we’re starting off easy here – this will pay dividends later on.
Defining What to Test
So far, it seems like we’re being somewhat arbitrary in our tests – while testing this input is good, and we do have a goal for writing a test to run through a sample game, we should have a few “rigor rules” to guide our efforts to make sure we’re writing valuable tests and not just ad hoc queries to satisfy curiosity. This is where I like to refer to advice from Andy Hunt and Dave Thomas, the original Pragmatic Programmers.
In their book, Pragmatic Unit Testing in C# with NUnit, 2nd Ed., Hunt and Thomas wrap up their experiences for writing top-notch unit tests with three acronyms:
- Good tests are A-TRIP: Automatic, Thorough, Repeatable, Independent and Professional
- Write tests using your RIGHT-BICEP
- Ensure that boundary conditions are CORRECT
I won’t rehash the acronyms here (you can check out the link above), however, suffice to say that we need to write tests that focus on one thing at a time, can be run independent of one another (ie, they don’t require prior tests to be run first) and exercise the code’s extremities, ie. anything that might break and anything that does break.
Let’s Try Breaking MakeMove()
To round out this post, let’s try writing a test to push in bad input to the MakeMove() method to see what happens. I have no idea what to expect, so I decide to try this:
Running the test, I got the following results:
A-ha! Now we have grounds for a new assertion – the engine does appear to validate input, throwing an ArgumentException if things are out of whack. We can test for expected exceptions by adding the ExpectedException attribute to our test definition:
I’ve dispensed with writing this test to fail-first with a different exception, since my initial run provided me with the expected exception. An important thing to note here is that I’m not writing the tests to pass, but using my knowledge of the system and code as I go to validate assumptions – which can change later on, depending on refactorings.
Next: More tests, web-accessible source
For the next installment, I’ll be getting into more meaty tests to validate inputs using Hunt & Thomas’ unit test guidelines and I’m also going to look into setting up web access for the source tree to provide the latest versions of the code via Subversion as we go along. As part of this effort, I’ll be writing some tests that I’ll share to validate the moves in a historical game. Until then, as always I welcome input on the posts so far and any constructive criticisms. Cheers!