Thanks to my wife, this blog post is because of our morning tea discussion. She asked me to convert the discussion into a blog :)
- You start with a test for a smallest unit of code you want to deliver (At this point in time it doesn’t exist yet)
describe 'Arithmetic::Sum' do
describe '#add' do
it 'is expected to take 2 and 3 and return 5' do
expect(subject.add(2,3)).to eq(5)
end
end
end
- Ensure test fails with an error that
Unit of code
you are testing doesn’t exist yet.
Arithmetic::Sum not found error
-
Coding time...., write just minimum code necessary to make the test fail for the reason you expect.
- Create Arithmetic module
- Add class inside that module
- Add method
add
(for now its empty)
Code errors out saying nil and 5 are not equal.
- Coding time..., Now write the minimum code necessary to make the test pass.
def add(a, b)
return 5
end
- Test should turn green at this point.
- Add few more test expectations to deliberately make the test fail, and ensure that the failure messages are meaningful.
describe 'Arithmetic::Sum' do
describe '#add' do
it 'is expected to take 2 and 3 and return 5' do
expect(subject.add(2,3)).to eq(5)
end
it 'is expected to take 4 and 6 and return 10' do
expect(subject.add(4,6)).to eq(10)
end
it 'is expected to take 100 and 100 and return 200' do
expect(subject.add(100,100)).to eq(200)
end
end
end
- As Uncle bob says,
As the Tests get more Specific, the Code gets more Generic
. Enhance add method to be more generic which could handle all the test cases mentioned above.
module Arithmetic
class Sum
def add(a, b)
return a + b
end
end
end
- Run the tests again and see all of them turn green.
- Commit the code :)
Example given in this blog follows TDD approach i generally follow when writing ruby programs. Just curious how Java or Go community approach TDD as there is a compilation phase before running a program. I will research and if possible add another blog in continuation to this based on how their dev community approaches TDD.
Thanks to Uncle Bob and his clean coder videos :)
Top comments (2)
The TDD cycle needs another step: Think, before writing a failing test. That's clearly stated in the C2 Wiki where TDD was first defined, but somehow everyone forgets that step.
In compiled languages you use your tests to design your API: so, you describe in your tests how your API will look and feel, and then before running it for red, you write enough structure to make it compile.
In strongly-typed languages (Scala, Haskell) you may even design types that match so well the domain that if a program compiles, it is probably correct; the types prohibit you to even represent an invalid state of the system. If you can exclude most errors because you can't write them in code (the compiler blocks you when it checks the types), you can afford to write much less tests.
I also suggest you to view 7 minutes, 26 seconds, and the Fundamental Theorem of Agile Software Development: it's a short, but brillant piece of thather that will give you another perspective on this.
Interesting...
Thanks for that Andy