So there are a lot of advantages to using TDD, they are well documented and there are plenty of people who extol its virtues.
One of them however has resonated with me of late. And that is how it effectively builds a living document of the implemented functionality of your system.
I am working on a personal project, and the time I can spend on it is quite sporadic. Between work and a young family, time to code for fun is quite broken - 10 minutes here, an hour there, a weeks break, another 10 minutes and so forth. It doesn't make for much of a flow.
But what I have found is, by sticking to TDD and by using test names that are informative that I have built a living document of what I've implemented. So when I come back to the code even for a moment I know where I am. Run the tests, and green, there is all my functionality.
Better yet, the breaks force me to revisit my naming, and I actively refactor the code (including the tests) to try and make it more readable.
I've also found that by invoking the "if its not green at then end of the session then ditch it" rule quite effective too. Now you might say that if you only have five minutes to work on it then chances are you're not going to get to green. And a lot of the time its true - but before I stop, I roll back to green.
Now at first that seems counter productive, as the tests will show where its broken and you know where to come back to - but in practice I have found that it actually tends to mislead me, particularly if I have been away from it for a while. So better to return to a clean slate. Then what I find is that my subconscious tends to chew on the problem for a while, and when I come back to it I find my flow much quicker.
So its just another reason to use TDD and descriptive test names.
Some random thoughts, usually about software, systems and process.
All views expressed are mine and not necessarily those of my employers past, present and future.
Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts
Monday, 18 May 2009
Saturday, 7 March 2009
Test Driven ...
In the past I have been guilty of not testing enough (who hasn't). I went through a phase of code first and test as late as possible, then code first and write tests - the old idea of unit testing. However this always ends up in testing getting left behind as time pressures push the demand towards just getting the code done.
I was then introduced to Test Driven Development, it made sense, but I struggled with it. Even when I thought I was doing it right I would find as time pressures increased that it would slip, and it wouldn't be long before I was back in the old style of unit testing.
I used to grapple with design, either in my head or on paper, then write a test against that design and then try and code that design. The problem being, that the design was never quite right. Some requirement was always missed or not considered, or even worse non-required features would slip in because they seemed like a good idea. Going back and changing that design, meant going back and changing the tests.
The other problem was that the tests were always biased towards that vision instead of testing the requirements, increasing testing became a matter of just adding more and more tests, a lot of them testing the same thing and of dubious value.
So as a result things never flowed, there was always a discontinuity - and that leads to turbulence and drag. Drag means wasting energy, there in the temptation to ditch that perceived drag, the testing, instead of the real drag, a bad design.
I then had the pleasure of working on a project with some really focused guys (Jim, Pat, Alistair amongst others), those who knew what TDD really was, and how to really do it, how to use the tools in the right ways. To them it was just natural, everything flowed, there was no drag.
The 'click' was the realisation was not that I didn't know how to write unit tests, nor was it that the test had to be written before the code, but the test had to be written before the design had crystallised.
Now, I am not saying that the secret is no design. Indeed design is very important, but now I tend to do enough design when and as its needed. The final design emerges as I test, code, test, refactor, test.
I was then introduced to Test Driven Development, it made sense, but I struggled with it. Even when I thought I was doing it right I would find as time pressures increased that it would slip, and it wouldn't be long before I was back in the old style of unit testing.
I used to grapple with design, either in my head or on paper, then write a test against that design and then try and code that design. The problem being, that the design was never quite right. Some requirement was always missed or not considered, or even worse non-required features would slip in because they seemed like a good idea. Going back and changing that design, meant going back and changing the tests.
The other problem was that the tests were always biased towards that vision instead of testing the requirements, increasing testing became a matter of just adding more and more tests, a lot of them testing the same thing and of dubious value.
So as a result things never flowed, there was always a discontinuity - and that leads to turbulence and drag. Drag means wasting energy, there in the temptation to ditch that perceived drag, the testing, instead of the real drag, a bad design.
I then had the pleasure of working on a project with some really focused guys (Jim, Pat, Alistair amongst others), those who knew what TDD really was, and how to really do it, how to use the tools in the right ways. To them it was just natural, everything flowed, there was no drag.
The 'click' was the realisation was not that I didn't know how to write unit tests, nor was it that the test had to be written before the code, but the test had to be written before the design had crystallised.
Now, I am not saying that the secret is no design. Indeed design is very important, but now I tend to do enough design when and as its needed. The final design emerges as I test, code, test, refactor, test.
Monday, 2 March 2009
Using Groovy and GUnit to test Java 'privately'
Normally if I am writing a class and find I need access to a private field in order to test correctly then I take it as a smell - something not quite right with the design. But every now and then I fail to find a way to change the design without corrupting its intent.
Now I know you can use reflection in Java to access private fields, but its a bit clunky. But recently I have been reading about Groovy, and found that it gives you full access to all your Java objects, but allows you to ignore private specifiers.
So as an example, say I have a counter class, one which allows users to get the next value of the counter, but cannot provide anyway of altering the counter for that would invalidate its purpose.
Now I could do that with reflection, but I can do it much simpler using Groovy (which if you look closely, especially the way I have written it, looks like java) ...
Unfortunately I have found some issues with the Eclipse Groovy Plugin - its auto-completion is a bit zealous in someways (wants to try and match everything bringing your machine to a crawl) and ignorant in others (not matching in imports). Also the GUnit results were reporting exceptions the line above the line with the issue - which caused me some confusion initially, and I need to investigate that further.
But to me this is a neat way of testing Java code, by stepping out of Java to something that makes it easier, whilst staying on the JVM.
Now I know you can use reflection in Java to access private fields, but its a bit clunky. But recently I have been reading about Groovy, and found that it gives you full access to all your Java objects, but allows you to ignore private specifiers.
So as an example, say I have a counter class, one which allows users to get the next value of the counter, but cannot provide anyway of altering the counter for that would invalidate its purpose.
package net.usersource.example;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private final AtomicInteger counter;
public Counter() {
counter = new AtomicInteger(0);
}
public int getNext() {
return ensuringValueDoesNotRollIntoNegativeValues(counter.incrementAndGet());
}
private int ensuringValueDoesNotRollIntoNegativeValues(int value) {
if( value < 0 ) {
if( counter.compareAndSet(value, 0) ) {
return 0;
}
else {
return getNext();
}
}
return value;
}
}
Now, how do I test this without calling getNext() until it reaches Integer.MAX_VALUE? Do you know just how big MAX_VALUE really is, and how long that will take? Thats not really an option - so I need somehow to change the underlying counter.Now I could do that with reflection, but I can do it much simpler using Groovy (which if you look closely, especially the way I have written it, looks like java) ...
package net.usersource.example
import org.junit.Test;
import org.junit.Assert;
import java.lang.Integer;
public class CounterTest {
@Test
public void verifyFirstObtainedCounterValueIsOne() {
Counter c = new Counter();
Assert.assertEquals( c.getNext(), 1 );
}
@Test
public void verifyThatAtMaxIntTheNextValueIsZero() {
Counter c = new Counter();
c.counter.set(Integer.MAX_VALUE-1);
Assert.assertEquals( Integer.MAX_VALUE, c.getNext() );
Assert.assertEquals( 0, c.getNext() );
}
}
This was TDD'd so I only knew the solution thanks to the test.Unfortunately I have found some issues with the Eclipse Groovy Plugin - its auto-completion is a bit zealous in someways (wants to try and match everything bringing your machine to a crawl) and ignorant in others (not matching in imports). Also the GUnit results were reporting exceptions the line above the line with the issue - which caused me some confusion initially, and I need to investigate that further.
But to me this is a neat way of testing Java code, by stepping out of Java to something that makes it easier, whilst staying on the JVM.
Saturday, 28 February 2009
Is JUnit Named Right?
When JUnit first appeared, it was perfectly named, however as time has moved on so has its use.
Jim Webber's recent blog post (here) made a very valid point about when a Unit Test is not really a Unit Test. Test and Behavioural Driven Design push JUnit into an area that is not adequately described as unit testing. Driving a design through testing in this manner means you aren't (at least initially) writing unit tests.
I've also used JUnit in the past to do things which are decidedly not unit testing, its a useful framework to control other sorts of testing. And on more than one occasion have had to explain to a member of management why I used a free unit testing framework instead of using their big expensive shiny testing metaframework thingy.
I am not suggesting that Kent and Eric should rush out and re-release their work under a different name. It was (to me at least) interesting to reflect on naming, and how things progress over time, so once perfectly named entities may gradually slip to the almost perfect. And it is also a reminder that not quite perfect risks gradually slipping to the not relevant at all.
What the perfect name would be today, I don't know, I think the name is still perfect enough.
Jim Webber's recent blog post (here) made a very valid point about when a Unit Test is not really a Unit Test. Test and Behavioural Driven Design push JUnit into an area that is not adequately described as unit testing. Driving a design through testing in this manner means you aren't (at least initially) writing unit tests.
I've also used JUnit in the past to do things which are decidedly not unit testing, its a useful framework to control other sorts of testing. And on more than one occasion have had to explain to a member of management why I used a free unit testing framework instead of using their big expensive shiny testing metaframework thingy.
I am not suggesting that Kent and Eric should rush out and re-release their work under a different name. It was (to me at least) interesting to reflect on naming, and how things progress over time, so once perfectly named entities may gradually slip to the almost perfect. And it is also a reminder that not quite perfect risks gradually slipping to the not relevant at all.
What the perfect name would be today, I don't know, I think the name is still perfect enough.
Subscribe to:
Posts (Atom)