Notes from Effective Testing with RSpec 3, chapter 3.
Out of the half dozen listed benefits of TDD, I connected with two of them.
Enabling Refactoring/Create confidence
There have been moments where I completed a feature with 20-30 minutes left in the day. Enough time to clean up my code. This could lead to application failures surprising in scope or process. Scrambling to revert my changes I would end the day lost and confused. With tests already written, refactoring is a rewarding process. Having immediate failure in context teaches me where I am wrong about the application or technology.
Writing tests get’s the abstract concepts out of our heads and into a concrete plan. It helps us recognize better design and separate ideas. Effective Testing points out that if writing specs for your current code base feels painful then it indicates the code is difficult to maintain and is an opportunity for refactoring.
When a test suite is slow it translates into a monetary cost. If a developer is waiting 4 minutes for a tests suite to run there is an additional cost as that developer eases back into their workflow.
Tests that break easily or throw false positives are another time sink. It takes time and cognitive ability to understand the scope of the problem and how to resolve it.
An overdone test suite contains all of the above problems in addition to maintenance. Effective Testing quotes Kent Beck from stack overflow, I believe this is the quote I was thinking of from chapter 1,
I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence…
A test creates a dependency on your codebase. If that bit of code is modified or removed you also have to update that test. Effect Testing recommends avoiding this dependency as best as you can. One way is to not tests code that frequently changes such as a user interface. Another way is to generalize your tests. Instead of looking for an ‘exact match’ check if the value is included.
Sandi Metz presents an additional view for writing tests. Separate tests from your public and private interface. We should only be concerned with the accessible parts of our classes. She keeps tests for private methods in a separate file. In a comment it instructs the reader to not fix failing tests but delete them. Private methods are meant to support the public. They are likely to change more frequently and drastically over their public siblings.
RSpec supports many different types of tests but Effective Testing will focus on three.
End to end testing. I’ve written about acceptance testing before. It’s a great way to lay some ground coverage and start refactoring.
It’s not ideal for long term maintenance as it tends to be brittle and change frequently as an application changes.
Unit specs are tests that describe a class or methods behavior. These seem to be the most common type of test. It’s the fastest turn around time for writing a spec, failing it, making it succeed, and then refactoring.
These tests aren’t as useful for larger scopes of refactoring. They often describe your application in bite sized chucks but not a feature from end to end.
Integration specs sit in-between acceptance and unit. This is the code that interacts with external services and api’s. In Ruby on Rails projects we use integration tests to describe behavior at the controller layer.
The authors of Effective Testing have prepared us with the benefits and costs of testing. They have described three different types of tests with the benefits of each. With the basics of RSpec at hand we will spend Part 2 of the book diving deeper into Acceptance, Integration, and Unit testing.