Archive for November, 2008
TDD: If it's hard to test reflect on your approach
Chad Myers gets it spot on in his recent post about not testing private methods – private methods are private because they should be inaccessible from outside the class and their functionality should be tested via one of the public methods that calls them.
I've found that when a piece of code seems really difficult to test without exposing a private method then we're probably trying to test that functionality from the wrong place.
I came across just this problem earlier this week when trying to test the functionality of one of our controllers – we are using the ASP.NET MVC framework to build our application.
My first approach was to try and test everything from the controller test but it quickly became apparent that this was going to be very difficult as the only data we had access to was the ActionResult which was insufficient for our testing purposes.
It became clear that if we put the logic into one of the objects being created inside the method (by a WCF web services call) that it would be significantly easier to test the logic – so that's what we did! Suddenly it became much easier to test and we were able to come up with other test scenarios that we hadn't previously considered.
The lesson to learn here is to constantly reflect on whether we are testing from the right place and if it feels too difficult then it probably is.
Another testing lesson I learnt recently was not to override protected methods in order to test other logic in the class. This is arguably even worse than exposing private methods because now we are not testing the production code but a variant of it.
The reason I did it was because all the logic for preventing caching of data was encapsulated into a single method and since I wasn't interesting in testing that part of the code I thought it would be fine to just ignore it for those tests.
Nick pointed out that the actual problem was that we were missing an abstraction and as a result had ended up creating a dependency that made testing difficult. In this case the object was dependent on HttpResponse.
The lesson here is to ensure that objects depend on abstractions and not on concrete implementations wherever possible.
The underlying lesson in both cases being that we need to be constantly reflecting and questioning our approach to ensure that we write clean code and test that code in a clean way.
Coding Dojo #4: Roman Numerals
We ran our 4th coding dojo on Thursday night, attempting to solve the Roman Numerals problem from the TDD Problems website.
The Format
We ran with the Randori approach again with between 4-6 participants taking part. We coded for about an hour and a half.
The pair coding were sat at the front of the room this time in an attempt to keep the focus on the code, a problem identified last week.
What We Learnt
- I had worked on this problem myself a couple of weeks ago but it was interesting to note how many more of the edge cases we managed to cover when working as a group. We had an discussion around where the validation of what constitutes a valid roman numeral should go. We ended up validating when the roman numeral object was converted into an integer, the other option being to do constructor validation. I'm not sure exactly when each approach is applicable – I would tend to do constructor validation when data is coming in from the user interface otherwise validating when the object is used.
- Having the pair at sit at the front of the room worked much better in terms of stopping others drawing on the white board and we seemed to achieve greater focus on the problem than in the previous weeks although I'm not sure whether or not this is directly linked to the positioning of the coding pair.
- The idea of keeping minimal state in the code was an interesting idea that was suggested early on and one which we kept to. I've not considered this as part of my approach to solving problems before but it makes sense in helping to drive simplicity of design.
- The drive for the green bar was again in evidence – on one occasion I tried to refactor and make the test pass in one go, forgetting that this would keep us away from the green bar for longer. This was pointed out by my pair and I backtracked the refactoring and just made the bar green.
Html.RadioButton setting all values to selected value workaround
While working with the Html.RadioButton() UI helper for ASP.NET MVC we came across an interesting problem whereby when you submitted the form, all the values for that particular group of radio buttons was set to the value of the one that was selected.
For example, given a form like this:
1 2 | <%= Html.RadioButton("option1", true) %>Yes <%= Html.RadioButton("option2", false)%>No |
When we first load the page, this is the HTML it generated:
1 2 | <input type="radio" name="option1" value="true" />Yes <input type="radio" name="option1" value="false" />No |
When we post the form having selected the 'Yes' option for example, this is what the HTML looks like now:
1 2 | <input type="radio" name="option1" value="true" checked="checked" />Yes <input type="radio" name="option1" value="true" />No |
A bit of Googling revealed that others have come across this same problem and that it is a bug in the code.
The solution suggested on Stack Overflow was to write a custom RadioButton helper which does a regex replacement on the 'value=' part of the HTMl generated.
We started working down the path to using a similar approach before James pointed out that we might be able to achieve the same outcome by passing in the value as one of the htmlAttributes.
We changed our original Html.RadioButton() code to take in 'new { value = true }' and 'new { value = false }' respectively and it solved the problem.
TDD: Suffering from testing last
I've always been a big proponent of writing tests before writing code, and I roll off the standard reasons to people who question this approach:
- They help to drive the design
- They provide a safety net when making future changes
- They provide a way of communicating the intent of the code to the rest of the team
And so on. Despite knowing all this I recently took a non test driven approach to writing some bits of code – we were keen to get the system working end to end so it seemed a trade off worth making to prove that it was doable.
I knew that I would suffer when it came to testing this code after I had already written it and indeed it did!
I've tested around legacy code before and although Michael Feather's book makes it much easier, it's still not a great experience. The saving grace has always been that I didn't write the code whereas in this case it was quite definitely my fault that I ended up with this problem.
The original pain came from not knowing where I should start. Since we didn't have any tests I wasn't sure exactly what the code was doing despite the fact that I had written most of it fairly recently.
In particular I ran into the problem whereby I wrote a test which passed so all seemed good. Suspicious as to whether this was the case I decided to comment out some of the code which was apparently making the code pass. I expected the test to fail and give me the red bar but in actual fact the test still passed!
It turned out that I had written the expectations for my mocks slightly wrong in the test – a mistake that I would have definitely caught if I had written the test first but could have easily missed by testing last.
After this I decided that because there wasn't too much complex logic in this particular piece it would be much better to comment out the code and then test first before fitting it back together again. Eventually I got all the code covered but it took much longer than if I had just tested it properly the first time around.
For me software development is all about creating quick feedback loops and I find it very frustrating when I'm unable to do this. Testing first is one very good way of achieving this as it provides a clear way forward and gives feedback on our progress.
I will certainly be very reluctant to test last again.
Dave Thomas on Cloud Computing
I went to see Object Mentor's Dave Thomas give a talk about cloud computing on Tuesday evening in a combined meeting of the Sydney Alt.NET user group and several others.
I'd not seen him speak before but several colleagues had seen him at JAOO earlier this year so he came highly recommended.
We started off with a plug for the JAOO Australia 2009 conference which will again be in Brisbane and Sydney at the beginning of May. I've not been to a JAOO conference before but just looking through last year's slides and looking at the quality of the speakers is enough to tell you it's worth attending.
After that we moved onto his view on cloud computing, which I'm told was quite similar to one he gave at JAOO called 'Next Generation IT – Life After Jurassic Middleware'.
I've heard about cloud computing but not much more beyond that so it was quite the learning experience for me. Some of the more interesting things he spoke about:
- The opening part of the presentation spoke of the pain that we have created for ourselves in the software world with over complex solutions to problems which the majority of the time are just moving data from A to B and doing CRUD operations. He was particularly damning of Java and ORM in this section of the talk.
- The main idea around cloud computing was that we should be able to develop and deploy applications quickly. We shouldn't have to rely on a production support team to deploy and take care of our application i.e. throw it over the fence, but should take responsibility for it. Deploying to the cloud gives us the ability to do this.
- Services should be all about having a simple API and hiding the complexity behind this so that the consumer doesn't have to worry about it. I guess this ties in quite tightly with encapsulation, but he spoke about the leaking of complexity that we see in many APIs which make them much more difficult to use.
- One idea I found interesting was that of exposing legacy systems as atom feeds. We often spend a lot of time trying to add tests around these systems to allow us to add new functionality, but this approach seemed to suggest just using them for their data and writing the code elsewhere.
- Javascript is the language of the future if Dave Thomas is proved correct. I have certainly been doing more Javascript work recently, in particular using jQuery. As Dave mentioned, the tools are not quite there for Javascript development but he believe they will be in the next year or two.
- In response to a question about some of the new features being planned in Java 7, he spoke of the need to choose the right language for the job and his dislike of the current trend for object oriented languages to support functional programming concepts. The obvious thoughts here for me were that when it comes to parallel computing Erlang is best, for web development, Ruby, for client side development, C#. Of course we don't always have the choice when it comes to language as clients have to maintain what we have written but in an ideal world his ideas make sense.
Overall a humorous and interesting talk and one that has made me intrigued to learn more about cloud computing.
Agile/Lean: All or Nothing?
While reading The Toyota Way one of the ideas which stood out for me was the constant mentioning of organisations which picked bits of The Toyota Way, implemented them, achieved some short term gains but then eventually these improvements and went back to the way they were before.
I noticed a similar theme coming out in the series of posts in the last week or so about the decline of agile.
I have worked on several projects over the last couple of years with varying levels of agile being applied. I am aware that agile is considered to be something that you are rather than that you do but for this post agile refers to agile principles and practices.
I have noticed that it is much easier to get working software out the door when we follow these principles and practices but becomes way more difficult as soon as we are unable to follow some of them.
This is a similar idea to one I noticed in The Toyota Way:
Early on in the book the TPS House was mentioned – the idea being that everything must be strong from the use of the tools to the belief in the philosophy. It seemed to me from reading this that applying Toyota's ideas successfully is an all or nothing game – i.e. it would be very difficult to be successful in the long term by just picking and choosing certain ideas without having the other ones to support them.
One of the common misunderstandings about agile is that writing tests makes us move more slowly and that removing them will allow us to go more quickly. While this may be true in the short term it eventually catches up with us and we can look forward to long sessions with the debugger trying to work out why the code isn't working as we expected.
A couple of months ago I was considering whether there are any agile practices which are absolutely key to ensuring success. I ran this idea by several colleagues who all pointed out that it was the principles that were absolutely key and not necessarily the practices which are derived from them.
I believe we can therefore make a link between the practices and principles of agile, such that if we don't believe in thoroughly testing our code, for example, then we are not adhering to the principle of delivering working software or paying continuous attention to technical excellence.
Having said that I have also worked on projects where delivering frequently was not something which the business considered beneficial and we only delivered software at the end of the project. We did take the time to carry out practice deployments more frequently than that to ensure we knew the deployment process but there was no actual deliverable until right at the end, and despite not completely adhering to this principle we were still able to successfully deliver.
As Jeremy Miller points out in his response to the original post:
In the early days, XP was roundly criticized because every practice only seemed to be safe due to the existence of the other practices. Take one out, and the others weren't that great by themselves. I think that's more or less a fair criticism, but my response is simply to adopt, and effectively apply, everything you need to make Agile development successful.
Life is made much easier if everyone involved into the project buys into the agile or lean approach but even if they don't it is still possible to find ways to succeed, it just might be a bit more difficult.
Lambda in C#: Conciseness v Readability
One of the things I really disliked when I first came across C# 3.0 code was lambda functions.
At the time I remember speaking to my Tech Lead and expressing the opinion that they were making the code harder to understand and were valuing conciseness over readability.
After a week of reading about the new C# features and understanding how they worked the code was now more readable to me and a lot of the boiler plate code that I had come to expect was no longer necessary.
My favourite example of the power of lambda is when we want to iterate through a collection of items and apply the same operation to every item in the collection.
In normal C# we might do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Foo { private String bar; private String baz; public Foo(String bar, String baz) { this.bar = bar; this.baz = baz; } public override string ToString() { return string.Format("{0} - {1}", bar, baz); } } |
1 2 3 4 5 6 7 8 9 | var foos = new List<Foo>(); foos.Add(new Foo("bar1", "baz1")); foos.Add(new Foo("bar2", "baz2")); var fooString = new List<String>(); foreach (var foo in foos) { fooString.Add(foo.ToString()); } |
Using the power of C# 3.0 we can change that last for each statement to read something like this:
1 | var fooString = foos.Select(f => f.ToString()); |
This is much more concise but I think the judgement on its readability depends on one's understanding of the language feature.
One idea I am considering trying is using methods which describe more clearly what the lambda function is doing. This is an idea I came across from Kris Kemper's post about using similar Ruby language features.
In the example I gave perhaps wrapping the foo.Select(…) in a method called 'ConvertToStringRepresentation()' might make it more readable – it's clearly up for debate though.
When I was learning how lambda worked I found it useful to be able to do the comparison of how you would write code without it and being able to compare that with how you could do it with lambda. I also found having some understanding of how Ruby blocks worked made it easier as well.
Clearly having powerful language features means that a language is much easier to abuse but I think if used sensibly then readability and conciseness need not be mutually exclusive.
Testing Test Code
One of the interesting discussions that has come up on several projects I've worked on is whether or not we should test code that was written purely to help us test production code.
One of the main arguments used against testing test utility code is that it is not production code and therefore perhaps doesn't need to be held to the same standards because it lacks the complexity of production code.
Even ignoring this assumption, which I don't agree with, I think we are still missing the point as to why we actually test in the first place.
Although a lot of the value of the test driven approach is that it helps us to drive design, the other side of the story, which we perhaps only fully appreciate after working with legacy code, is the safety net that having a suite of tests around our code provides.
From my experience the complexity of the test utility code is what determines whether it is valuable to test it or not. If it's just builders to help create test data then I would agree there is little value in testing it, but if there's actual logic and/or complexity then having a safety net is a good thing. Even if the tests are very simple they help protect us to ensure our code is still working when we make future changes.
The thing to watch for is that test utility code often starts off being very simple but slowly complexity creeps in making it more difficult to verify whether it actually does what we want it to.
As a rough marker, when you end up debugging through your test utility code to work out why it isn't working as you expected it's probably time to consider putting some tests around it.
Agile: A reminder of the benefits of colocation
Sometimes it's the seemingly small details of the agile/XP approach to software development that make it so much more effective than the traditional approach.
I was reminded of this last week with regards to having co-located teams with the developers, BAs, QAs and the business people all sitting in close proximity.
I was working on the auto completion function for one of our screens and the QA on the team, who was sitting next to me, asked me if I could look through the acceptance criteria that he was working on.
This conversation was almost perfectly on cue because I was just about at the point where I had covered all the happy path cases and needed to understand the other paths. While looking through the acceptance criteria we needed some clarification with regards to exactly what the business wanted.
We went over to where the BAs were sitting and posed the question. Luckily they had just had the conversation with the business people and were able to explain the intent behind the criteria.
Interestingly writing this has made me realise that there are a couple of points in our process where more communication would make things flow more smoothly but seeing the speed with which we were able to go from confusion over a requirement to complete understanding was a really great reminder for me.
Coding Dojo #3: Krypton Factor
We ran our 3rd coding dojo on Thursday night, attempting to solve the Krypton Factor problem from the Online Judge website.
The Format
We ran with the Randori approach again, exactly the same as last week but this time we only had 4 participants for the majority of the coding session.
What We Learnt
- We still ended up spending a large percentage of the time drawing out the problem on the whiteboard and not coding. This seemed partly due to the difficulty of the problem and partly due to the layout of the room whereby the pair coding were sitting at the back and the people watching were sitting in front of them. We are going to try changing the layout so the pair at the computer are at the front of the room to shift the focus to the code.
- The problem we picked was very algorithmic in nature and we spent a lot of time discussing how to write an optimal solution to it rather than getting a naive one working first. It was eerily similar to the situation we sometimes end up with on projects where we are prematurely optimising a system before we actually have it working.
- The previous two coding dojos we had focused on a problem which was easier to understand and perhaps more suited to a TDD and Object Oriented approach. We are probably going to try and focus more on problems which we can understand more quickly therefore allowing us to focus more on the way that we solve them.
- One thing I learned personally when pairing with Phil was the need for clear naming of classes and methods. We had let this slip a bit as we struggled to progress with the problem to the point where the naming we had didn't really make any sense. When Phil came to the keyboard he pointed this out and we refactored the code until it made more sense.
- A common trend across the three coding dojos we have run so far is that we are always working with a very high level problem and struggle to break it down into smaller chunks that we could work on. It was suggested that perhaps breaking down the problem into stories might be more effective since this is what we are used to from our project work. We have often ended up with classes have a lot more than one responsibility due to our attempts to solve the problem from the first acceptance test that we write. Writing stories would help in our attempts to take baby steps on the way to solving the problem.