TDD + autotest = happy developers + quality code

In test-driven development (TDD), we make each test fail before we write the code that makes it pass. It’s a great practice because it helps you spot bugs almost instantly (esp. if you use “autotest” or a similar program to automatically run your tests every time you modify any code or test).

A few minutes ago, I wrote the following “failing test”:

it "won't overwrite an existing file" do
  FakeFS do
    File.open(house_appraisal_downloader.appraisal_dir + '/test_record.html', 'w') { |outf| outf.write("abcde") }
    expect {house_appraisal_downloader.save_as('test_record.html')}.to raise_error
  end
end

I expected my test to fail by overwriting the existing file and not raising an error. Instead, it passed — by raising an error when I hadn’t expected one since I hadn’t yet written the code to prevent overwriting existing files.)

To see the error, I ran:

it "won't overwrite an existing file" do
  FakeFS do
    File.open(house_appraisal_downloader.appraisal_dir + '/test_record.html', 'w') { |outf| outf.write("abcde") }
    house_appraisal_downloader.save_as('test_record.html')
  end
end

The error message pointed me straight to my goof. My code should have been:

def save_as(filename)
  File.open(@appraisal_dir + '/' + filename, 'w') { |outf| outf.write("12345") }
end

But I had forgotten to add , 'w' as the second parameter of File.open!

So thank you yet again, TDD. Writing failing tests before writing the code to make them pass is a brilliant way to catch errors immediately. It simultaneously builds a large test suite that documents your code and immediately identifies future code regressions.

Posted by James on Friday, September 09, 2011