Writing Tests: Moving from Methods to Classes
Once I decided to get started with TDD in Ruby, I developed a perhaps premature sense of pride in my ability to use TDD to write methods. Following the red-green-refactor model, I would write a test for a method, write the code to make it pass, revise for elegance and maintainability, and then repeat those steps until the method accomplished everything I wanted. But as soon as I got ambitious and started trying TDD with classes, I ran into some trouble: a NameError telling me of some “undefined local variable or method.”
What was going wrong? Well, as I progressed from files containing single methods to full classes, I was writing tests for instance methods without initializing an instance of that class upon which I would call those methods in my tests.
So, for example, lets say I wanted to write a method that returns “Hello, world!” Simple enough. First, I’d write a test in my project’s spec directory:
Watch it fail. Generate a blank file in my projects lib directory named hello_world.rb. Watch it fail again. Now add some code:
Run the tests again. Everything passes. Great!
But what if I wanted that method to be nested in a class? Well, I’ve already written the tests, so lets add the class and see what happens. Back to the hello_world.rb file:
Run the test and disaster ensues:
How can this be? I haven’t changed the method!
Well, now that I’ve nested my (instance) method inside of a class, the test needs to initialize an instance of that class upon which it can call my method. Changing the test like this solves our problem:
So, altogether, not too complicated. In order to test an instance method, your tests need to initialize an instance that can call that method.
Bonus side note: if you’re testing multiple instance methods, you can DRY up your code by initializing your instance just once:
Though, I must advise that you proceed with caution, as I’ve heard that relying on “let” isn’t necessarily a best practice.