Why
One of the things I see most on job descriptions, brought up in interviews, and thrown around in general programming discussion is Test Driven Development. For the most part, everyone understands how the methology works and how to implement it. But, I see a lot of developers miss out on the benefits when it comes to fixing a bug.
How
First, it needs to be said that every single bug should have at least one test in the pull request that fixes the bug. That’s a given, I hope, but we can expand on this.
When I bug rears its ugly head, the first thing that should happen is adding at least one test to prove the bug’s existence. Reproduce the bug with tests.
describe CustomAddingClass do
let(:amount) { 1 }
describe '#add_one' do
subject { CustomAddingClass.new(amount).add_one }
it { is_expected.to be(2) }
end
end
This is your test, and it passes. Look at our class:
class CustomAddingClass
def initialize(amount = 1)
@amount = amount
end
def add_one
@amount + 1
end
end
We expect CustomAddingClass#add_one
to increment the constructor arg by one, but it isn’t working. The
test passes, but reports are an error message: TypeError (no implicit conversion of Integer into String)
.
What gives? You could guess all day, and with this particular error message, you’d probably figure it out really quickly. This is just a simple example. Imagine the system is far more complex.
We need a test to reproduce the bug we’re seeing in production.
describe '#add_one' do
context 'when amount is a string' do
let(:amount) { '1' }
subject { CustomAddingClass.new(amount).add_one }
it { is_expected_to raise_error(TypeError) }
end
end
Ah, ha! We’ve found our bug. Somehow the frontend is allowing a string to be passed to CustomAddingClass#new
.
So, we update our class to ensure that we’re always dealing with an integer:
class CustomAddingClass
def initialize(amount = 1)
- @amount = amount
+ @amount = amount.to_i
end
def add_one
@amount + 1
end
end
And now our test fails because we no longer have an error when a string is passed into the constructor. So we update the new test to fit the new behavior.
describe '#add_one' do
context 'when amount is a string' do
let(:amount) { '1' }
subject { CustomAddingClass.new(amount).add_one }
- it { is_expected_to raise_error(TypeError) }
+ it { is_expected_to equal(2) }
end
end
All we’ve done here is replace is the assertion that the class will raise an error with the new assertion that we expect it to return the originally expected value.
And all tests pass again.
Huzzah! Now you can rest easy as you’ve proven how the bug manifests and the fix.
TDD implemented for bug fixing.