Apprenticeship -- Day 5

November 30th, 2018

Test Driven Development

Today I continued working through the TDD tutorials from Thoughtbot. I think I'm finally starting to understand the benefits of TDD. It promotes quality and productivity. I had heard about these benefits and trusted people enough to agree with them, but I didn't really understand it until today when it finally clicked. TDD is simply a technique that promotes writing failing tests, then writing test-passing code in increments. TDD is not writing code and then testing later when someone makes you feel guilty.

Incremental change allows steady and quality progress while protecting against the breaking of previously implemented code. You do not code first; additionally, you only solve the problem at immediate hand. I think I can see why TDD and OOP make a great pair. Writing small increments of code that will easily allow for changes seem pretty impressive to me.

So how does one go about TDD(ing)?

From what I understand, there is a cycle that involves writing a single failing test, writing code to pass that test, then refactoring your code for improvements while keeping tests passing. This is known as the "red-green-refactor" cycle. You want to begin in the red (failing) state and then enter a green (passing) state.

Here are the steps:

1. You write a test. If it fails, you're on the right track.

2. You write code that is simple enough to pass the test.

3. Pass your test.

4. Refactor your code and make sure that your test is still passing.

That's it. Well, this is it at the core. Of course there are books and papers out there just on these phases alone, but this is all you need to start.

Here is one of my first examples from Thoughtbot that helped me see this cycle. It involves creating methods for a Calculator class. Let's say that we have decided to use Ruby, we've set up Rspec, and we're ready to write a test for our first method that adds numbers. We start with empty ruby and spec files.

1. Require the ruby code file in the spec file:

Screen Shot 2018-12-14 at 8.43.22 PM

2. The first requirement is to add two numbers.

3. The following could be our first test:

Screen Shot 2018-12-14 at 8.44.23 PM

4. Run the test in the terminal with "bundle exec rspec spec" command. It will fail because the Calculator class does not exist in the calculator.rb file.

5. So add the Calculator class:

Screen Shot 2018-12-14 at 8.37.56 PM

6. Run the test again. It will fail because the add method does not exist in the calculator.rb file. So add that method:

Screen Shot 2018-12-14 at 8.36.33 PM

7. Run the test again. It will still fail. The add method in the test expects two numbers to be added. It's expecting two arguments on the add method and the current method in the calculator.rb files doesn't require any arguments. So add those arguments:

Screen Shot 2018-12-14 at 8.34.09 PM

8. Run the test again. It still won't pass because "2" is expected, but the method is currently returning nil. So implement the minimal code it needs to pass. Let's simply return "2":

Screen Shot 2018-12-14 at 8.29.45 PM

9. Yay! The test is passing! But does this method correctly add other numbers? What if we enhance our expectation? Let's amend to our test and see:

Screen Shot 2018-12-14 at 9.01.41 PM

10. Run the test. It will fail! The test is expecting return values "2" and "5". At this point, I added an operator so that both expectations would be met.

The key is to keep code clean and simple through feedback. People are scared to test. I used to be scared to test. How do you test the unknown? Your tests will tell you what to expect from your code without you having to look at or write your code. I can honestly say that TDD has helped me code in languages that I'm unfamiliar with. I'm sure with more practice, my code will be easier to read and maintain.