I first heard about unit tests at an open source developer conference in 2006 and since then I've been trying to understand this concept.
The first tests I wrote were with PyUnit, I don't remember what the project was, I just remember that it was horribly implemented, I didn't have any notion of the SOLID principles, so it was very difficult to write the tests and eventually I gave it up.
A couple of years later I started to develop a large enterprise application in .NET which forced me to improve my software architecture skills, I started reading a lot of books on the subject:
As I was reading the books I began to realize how little I understand about software craftsmanship and how much more there is to learn.
So I started writing tests again (still after the code itself), I could see the value of the tests, since it helped the stability of the application I was working on (to this day there aren't any new bugs).
But the tests still didn't feel right, they didn't cover all of the scenarios, they were annoying to write, I kept having to change the original after adding the tests.
Later on I wrote a todo.txt desktop editor in Python and QT, I looked for a testing framework and found doctest which allows you to write the tests inside the code (or in simple text files), like this:
def add(x, y):
"""
>>> add(0, 0)
0
>>> add(0, 1)
1
>>> add(1, 0)
1
>>> add(1, 1)
2
"""
return x+y
As you can see the docstring includes 4 tests (0+0, 0+1, 1+0 and 1+1).
This was my first attempt at TDD, it wasn't perfect, some tests were written before and some were written after the implementation, but I could feel I was starting to get it (you can see the git code in bitbucket.
A few years ago I started getting into Ruby on Rails, and the smartest thing I did was subscribe to Gary Bernhardt's Destroy All Software screencasts, most of what I know now about TDD I learned from these screencasts (so thank you Gary!).
I discovered an amazing testing framework called RSpec where you write your test in a much more readable format:
describe Cat do
describe "#talk" do
it "returns 'Meow'" do
cat = Cat.new
cat.talk.should == 'Meow'
end
it "returns 'Prrr' after eating" do
cat = Cat.new
cat.eat
cat.talk.should == 'Prrr'
end
end
end
When you run the tests this is the output:
Cat
#talk
returns 'Meow'
returns 'Prrr' after eating
The hierarchy and the plain text for examples and contexts make the output perfect as documentation for the code.
I learned that there are several kinds of tests:
I developed various Rails applications, and with each application my technique and my understanding of TDD improved, I could feel I was making progress. Back then I only wrote unit and integration tests.
I also started using a javascript testing framework called Jasmine which is basically RSpec for javascript.
At first, I was progressing slowly, working with TDD was much slower than non-TDD because I kept getting stuck on tests I wasn't sure how to write. But after a few months watching lots of screencasts, reading tons of blog posts on TDD, and spending a lot of time trying to improve my code and my tests I got to a stage where I'm working faster with TDD than without.
Whenever I write code without writing the test first, it just feels wrong and sloppy, and I can't make excuses anymore, since it doesn't take more time.
About a month ago I read The RSpec Book and it filled all of the holes, finally I had a full understanding of an entire BDD process that felt right to me:
As I understand it there are two ways to use Cucumber:
I know a lot of people don't like using Cucumber, and at first I thought so too, but after reading the RSpec book and trying it out I think that as long as you're using it as a BDD tool it can actually be more comfortable then rspec (for acceptance tests), I like the the Scenario Examples feature allows to you to tests multiple inputs in a pretty clean way:
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
| 2 | 5 | add | 7 |
| 0 | 40 | add | 40 |
The cucumber tests filled the gap between the user stories and the integration/unit tests for me and they help me stay much focused and a have a better indication of "done".
To sum things up, the three things I like most about this process:
Hope you enjoyed this post, thanks for reading,
David.