Much Respect for RSpec

Malina Tran
Tech and the City
Published in
5 min readJun 23, 2016
All hail RSpec.

You may have recalled my previous post, where I share my first experiences with testing and pair programming (yes, simultaneously). Since then, I have grown to somewhat embrace TDD and wholly appreciate its value to the programming. It makes the most sense in terms of having confidence in your code — especially when the code base starts to look murky. For instance, I was referencing a method that I had written earlier and checked the corresponding spec file to review its output value. Did it return `nil`, or did it return a value? Looking at tests for verification and documentation was entirely helpful. Looking at the method was not.

While this post is not meant to herald RSpec per se (particularly over Minitest — which I did touch upon in that prior post), I will be drawing examples from my experience with this framework. Admittedly, the biggest challenge was learning the syntax for RSpec. Getting a better handle of that helped me more toward writing code in the spirit of TDD.

Here are some basic lessons learned from using RSpec:

I learned how to handle private methods.

I am not inclined to make private methods, but learned from a software crafter that most methods should be private. If there is a helper method, make it private. And it is highly recommended to only test methods available in the public interface. In talking with a fellow resident apprentice, his rule of thumb is to make a method private if there is another method within the same class invoking it. Makes sense. The rationale behind having tests for public methods only is because they embody or rely upon private ones. By virtue of testing a public method, you are testing a private method. According to a Stack Overflow response: “A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation.”

If you think this gives you the leeway to forgo writing tests, oh how wrong you are! Through trial and error, I’ve learned that you must write tests in anticipation of a method. By writing a test, you are projecting its behavior and outcome. Even if you are envisioning a method that will be private, you must write that test. You must ensure its robustness. You must ensure that it will produce the output that you expected. (The only tests that I have not written are for methods that return a single string value). And even if you write a test that will be later deleted or enveloped into another test, it will not be for naught.

Apparently, there are ways to implement testing for private methods (also by using the `send` method). I have yet to see a purpose behind implementing this, but rest assured — it is possible!

I learned how to test for more than just equality.

During my first week as a resident apprentice, I was itching to write tests that went beyond testing for equality. Here’s an example of what my tests typically looked like:

context “#initialize” do 
it “initializes with a size based on user input” do
board = Board.new(size: 5)
expect(board.size).to eq(5)
end
end

This particular code applies to the `initialize` method and is followed by a “should” statement. Ninety-five percent of my unit tests have a message that begin with “should” and indicates what the test is supposed to achieve, if it passes successfully. I try to be as specific as possible without being narrow. While this above example is probably the best way to assure that a method produces the desired result, there can be creativity and variety in testing. I think it largely depends on the method’s structure and outcome, but I’ve also started using this one to test whether an outgoing message is being called:

user_interface = TicTacToe::UserInterface.newcontext “#request_move” do 
it “should call on a method to display the board” do
expect(user_interface).to receive(:display_board)
game.request_move
end
end

Note: you must call the method again setting the expectations. What is passed in the parentheses is the object (in the first set of parentheses) and the method that will be called within your method (in the second set). In this case, the object is `user_interface` which I’ve set in the first line and the method being called is `display_board`. Please remember to call the method in the last line!

I learned the importance of refactoring in my code and tests.

This is a critical one! What I’m learning to do unabashedly is to refactor. When a piece of code operates the way you hoped it would, or if a unit test passes, you may be ever so inclined to keep it that way. You may hold your breath, hoping it doesn’t break. (Yes, we’ve all been there). But don’t do it! If you think your code can be better, it’s most likely true. One of my main concerns about RSpec was that I found myself repeating lines of code. If you subscribe to the famous Don’t Repeat Yourself (DRY) principle, you should definitely apply it to RSpec as well. Perhaps my biggest takeaway is that — similar to life — writing code is an ever-evolving, improving process.

To provide context, RSpec implements before and after hooks which helps execute certain code blocks before and/or after the body. I found this to be helpful in streamlining lines of code, particularly through the use of `before` and `let`. I have been using the latter extensively, but to differentiate between the two:`before` creates instance variables and `let` creates local variables. The latter also does not exist until called into existence by the actual tests. On the other hand, before statements are run before each test and thereby increase load times. You can substitute “before” for “after” in the code snippet below:

before(:each) 
# run before each example
before(:all)
# run one time only, before all of the examples in a group

At the top of my unit tests, this is what I typically have to save myself from repeating myself and creating new variables for the same objects:

let(:board) { Board.new } 
let(:computer_player) { ComputerPlayer.new }
let(:human_player) { HumanPlayer.new }
let(:game) { Game.new(board, computer_player, human_player) }

Pretty simple and neat!

This may all be RSpec 101, but hopefully this blog post helped get you stoked about writing tests. I have previously documented my resistance and hesitation to TDD (it can be slow-moving, wrenching process!). But I think my relationship with it has dramatically transformed in the last few weeks. Now, it’s nothing but love and respect for RSpec.

--

--