Apparently, I'm a mechanic.
Friday, 23 January 2009
Wednesday, 21 January 2009
Cooking Up An Application
One of the things I like to do at the weekend is cook a nice meal. I enjoy the whole process of selecting and preparing the ingredients and then putting them together to produce something that (usually) tastes good and leaves me wanting more. It occurred to me, whilst I was walking the dog this morning 1, that software development is quite a lot like cooking in many ways. I did a quick search on the Web and chuckled to myself as I read the advice given to people who wanted to cook. You could almost take the advice, word for word, and apply it to software development. Let me share some examples.
The cook's version
"Nobody likes a dirty kitchen, and a dirty kitchen is no fun to cook in either (not to mention unsanitary). With a little conscious effort, you can keep your kitchen clean while you cook. "
The developer's version
Nobody likes dirty code, and dirty code is no fun to develop either (not to mention unsanitary). With a little conscious effort, you can keep your code clean while you develop.
The cook's version
Be organized before you start cooking. It is important to have all of your ingredients and equipment assembled before you start cooking. This will help you follow the recipe more easily and prevent any messing around looking for ingredients and equipment that you didn't realise you needed—or maybe don't even have!
The developer's version
Be organized before you start developing. It is important to have all of your requirements and tools assembled before you start developing. This will help you follow the recipe more easily and prevent any messing around looking for requirements and tools that you didn't realise you needed—or maybe don't even have!
The cook's version
Stick to the recipe. When you are a beginner cook, you should follow the recipe exactly. As you cook more, you will become more at ease with making changes to a recipe and still create successful dishes.
The developer's version
Stick to the patterns. When you are a beginner developer, you should follow the patterns exactly. As you develop more, you will become more at ease with making changes to a pattern and still create successful applications.
The cook's version
Clean up. It is easier if you can clean as you go but don't worry if this doesn't come naturally at first. After a while, you'll work out that this is easier than cooking in a mess! Just make sure that you clean up after yourself when you have finished cooking, and don't forget any spills on the floor or cupboard doors.
The developer's version
Clean up. It is easier if you can clean as you go but don't worry if this doesn't come naturally at first. After a while, you'll work out that this is easier than developing in a mess! Just make sure that you clean up after yourself when you have finished coding, and don't forget any spills in the test code.
The cooks version
Enjoy your experience. Cooking is fun and a creative activity. When you feel a little more confident, start experimenting with different ingredients and come up with your own creations.
The developer's version
Enjoy your experience. Developing is fun and a creative activity. When you feel a little more confident, start experimenting with different applications and come up with your own creations.
1 : If you find yourself needing thinking time buy yourself a dog. Larger ones need lots of walking and this is an ideal time to let your brain sort stuff out.
Monday, 19 January 2009
Duplication, NUnit, Row Tests and MVC Views
I'm working on a project that uses the ASP.NET MVC framework. Learning how to use this is fun and I like the fact that I am able to write code that feels cleaner to me. We are working with preview 4 at the moment but should be moving to the release candidate as soon as it becomes available. I'm really looking forward to some of the "post preview 4" features, such as Model Binder support and the AcceptVerbs attribute. One of the nice things about developing with this framework is that it makes unit testing Web sites a whole lot easier. This is because the "controller" part of MVC is just a standard class that you can write unit tests against.
I came up with a neat piece of code as I was writing a test that checked the value of a variable stored in ViewData. I'm not saying it's new or original, just that it evolved naturally from my use of Test Driven Development (TDD). The value being checked was an enumeration, which was used to control an aspect of the Web page style. The value was different for each action on the controller and we had eleven actions. I wanted to test that the expected value was present in the view returned from each action. I have an example of the sort of thing that I was trying to do that you can download from my Google Code project. Here is the unit test I wrote for the example (I use 'MethodUnderTest_Scenario_ExpectedBehaviour' as my test naming convention).
[Test]
[Category("Unit")]
public void Index_IndexActionCalled_ViewDataContainsExpectedColour()
{
// arrange
var theExpectedColour = Colour.red;
// act
var homeController = new HomeController();
var viewResult = homeController.Index()as ViewResult;
// assert
Assert.That(viewResult, Is.Not.Null);
if (viewResult == null || !(viewResult.ViewData["Colour"] is Colour))
{
Assert.Fail("ViewData does not contain a Colour.");
return;
}
var actualColour = (Colour)viewResult.ViewData["Colour"];
Assert.That(actualColour, Is.EqualTo(theExpectedColour));
}
Once I made this test pass I moved on to the next one.
I got this second test passing and noticed that there was a lot of duplication in my test code. It is good TDD practice to treat test code the same as production code, so my next step was to refactor the test code. The sections outlined in blue above show the code differences and those differences are 1) the expected colour and 2) the name of the method being called. To refactor this I would need to have a test that takes parameters. I could then pass in the expected colour and the name of the method to be called. Reflection could then be used to call the method. This is where NUnit RowTest attributes come into play. RowTest attributes are part of nunit.framework.extensions.dll. Adding a reference to this dll allows me to create parameterised unit tests. The next stage of my refactoring was to implement a parameterised unit test.
[Test]
[Category("Unit")]
public void About_AboutActionCalled_ViewDataContainsExpectedColour()
{
// arrange
var theExpectedColour = Colour.blue;
// act
var homeController = new HomeController();
var viewResult = homeController.About() as ViewResult;
// assert
Assert.That(viewResult, Is.Not.Null);
if (viewResult == null || !(viewResult.ViewData["Colour"] is Colour))
{
Assert.Fail("ViewData does not contain a Colour.");
return;
}
var actualColour = (Colour)viewResult.ViewData["Colour"];
Assert.That(actualColour, Is.EqualTo(theExpectedColour));
}
The method signature was changed to take the name of the method to be called and the expected colour. The call to homeController.About() is now carried out using reflection. This test passed and so I added a new Row attribute to test the call to homeController.Index().
[RowTest]
[Row("About", Colour.blue)]
[Category("Unit")]
public void About_AboutActionCalled_ViewDataContainsExpectedColour(string methodName, Colour expectedColour)
{
// arrange
var theExpectedColour = expectedColour;
// act
var homeController = new HomeController();
// Use reflection to call the action method
var controllerType = homeController.GetType();
var methodToTest = controllerType.GetMethod(methodName);
Assert.That(methodToTest, Is.Not.Null, string.Format("No method called {0} found.", methodName));
var viewResult = methodToTest.Invoke(homeController, null) as ViewResult;
// assert
Assert.That(viewResult, Is.Not.Null);
if (viewResult == null || !(viewResult.ViewData["Colour"] is Colour))
{
Assert.Fail("ViewData does not contain a Colour.");
return;
}
var actualColour = (Colour)viewResult.ViewData["Colour"];
Assert.That(actualColour, Is.EqualTo(theExpectedColour));
}
Adding new tests for this scenario simply becomes a case of adding more Row attributes. I made a couple of final changes before I was happy with this test. I felt the reflection code distracted from the intent of the test, so I used the "Extract Method" refactoring to pull that code out into a separate method. I also renamed the methodName parameter to actionName. Using methodName wasn't wrong, I just feel that actionName describes the intent of what is being tested better. We are calling action methods on a controller. It's just a little more explicit than saying we are calling methods on a controller. Also, my naming convention is not quite right for this test because About is no longer the method under test. I decide to use HomeController_ActionCalled_ViewDataContainsExpectedColour as a method name to describe the intent of the test.
[RowTest]
[Row("About", Colour.blue)]
[Row("Index", Colour.red)]
[Category("Unit")]
public void About_AboutActionCalled_ViewDataContainsExpectedColour(string methodName, Colour expectedColour)
{
// arrange
var theExpectedColour = expectedColour;
// act
var homeController = new HomeController();
// Use reflection to call the action method
var controllerType = homeController.GetType();
var methodToTest = controllerType.GetMethod(methodName);
Assert.That(methodToTest, Is.Not.Null, string.Format("No method called {0} found.", methodName));
var viewResult = methodToTest.Invoke(homeController, null) as ViewResult;
// assert
Assert.That(viewResult, Is.Not.Null);
if (viewResult == null || !(viewResult.ViewData["Colour"] is Colour))
{
Assert.Fail("ViewData does not contain a Colour.");
return;
}
var actualColour = (Colour)viewResult.ViewData["Colour"];
Assert.That(actualColour, Is.EqualTo(theExpectedColour));
}
[RowTest]
[Row("Index",Colour.red)]
[Row("About",Colour.blue)]
[Category("Unit")]
public void HomeController_ActionCalled_ViewDataContainsExpectedColour(string actionName, Colour expectedColour)
{
// arrange
var theExpectedColour = expectedColour;
// act
var homeController = new HomeController();
var viewResult = CallControllerMethodAndReturnViewResult(homeController, actionName) ;
// assert
Assert.That(viewResult, Is.Not.Null);
if (viewResult == null || !(viewResult.ViewData["Colour"] is Colour))
{
Assert.Fail("ViewData does not contain a Colour.");
return;
}
var actualColour = (Colour)viewResult.ViewData["Colour"];
Assert.That(actualColour, Is.EqualTo(theExpectedColour));
}
private static ViewResult CallControllerMethodAndReturnViewResult(Controller controller, string methodName)
{
var controllerType = controller.GetType();
var methodToTest = controllerType.GetMethod(methodName);
Assert.That(methodToTest, Is.Not.Null, string.Format("No method called {0} found.", methodName));
return methodToTest.Invoke(controller, null) as ViewResult;
}
I like the result of this refactoring. The code is neat, compact and easily understandable. Any developer looking at this code should quickly be able to understand the expected behaviour of the code under test.
Friday, 16 January 2009
SVNX Security Certificate Problem
I'm looking at how I can usefully use Google products. Someone at the company I am working at uses Google Code to host code examples, which seems like a good idea to me. I set myself up with a Google Code account, fired up SVNX and tried to connect to my new SVN repository. The connection failed because the certificate issued by Google was not trusted by SVN. A quick Google search came up with Importing SSL certificates into svnX . This post explains how to use the command line to import a certificate and after following the instructions I was able to connect successfully to the repository.