Nathaniel Wroblewski in the Machine Age

Well-Tested Ruby - Intro to Unit Testing

Test-driven development (TDD) is a style of programming where the developer writes specs/tests before writing a line of code. The developer ensures the test is failing, writes the minimal amount of code to make the test pass, and then refactors (cleans up the code). TDD forces the developer to think about the design of the program and the interfaces before writing any code. TDD also makes it easier to change or refactor existing code, because the developer will immediately know if the changes he or she introduced broke any existing functionality.

The best argument I’ve heard for TDD came from Robert Martin, a Java developer, speaking at Rails Conf ‘09 (see: What Killed Smalltalk Could Kill Ruby). Robert argued that ruby is nice because it’s so easy to write code in, it gives the developer a lot of freedom, too much freedom in fact. “It’s just so easy to make a mess.” It allows developers to hack out solutions with celerity because it’s so easy, but the result of such spikes quickly becomes unmanagable, and this same problem is what invariably killed smalltalk, a ruby predecessor. He offers TDD as a restraint, a discipline that could save ruby from a similar fate.

Really, the benefits of test-driven development (TDD) are self-evident once you adopt it, but switching to a TDD style of programming can be daunting. Hopefully, this post helps make TDD more approachable and understandable.

Unit Tests

The easiest way to get into TDD is writing unit tests. Unit tests ensure the atomic pieces of code you write behave as you expect them to. If you’re using Rails, it’s simple: unit tests test your model methods. In general, if I’m going to touch the model layer, I make sure I have a spec for it. It’s especially easy to catch if you get in the habit of proofreading your own pull requests. Every method, class method or instance method, has a spec.

Let’s walk through a few examples. I’ll assume a familiarity with Rails and I’ll use the popular RSpec library, but really the principles are the same irrespective of language, and actually the syntax is often very similar (jasmine/rspec).

Our first problem is a simple one: to create an order that can be cancelled. Let’s TDD it. The first step is to create an order object.

spec/models/order_spec.rb

require 'spec_helper' # loads the test configuration, etc.

describe Order        # I'll explain the describe in a min

If I have guard running (hint hint), I can configure it to have tests run automatically for me when I alter files. Otherwise, I can run rspec spec in my console. When I do, I get the following error:

uninitialized constant Order (NameError)

Ignoring the contrived nature of this example, this is a real benefit of TDDing. I know exactly what I need to do next because the errors tell me exactly what to do next. Additionally, I write the minimal amount of code to get the system working. In this example, I can make the corresponding order class. In rails, if it’s a model, I also need to set the inheritance chain.

app/models/order.rb

class Order < ActiveRecord::Base

end

Now, I run the test, and nothing explodes.

The next step is to define a method on the order class that will cancel the order. Let’s keep it simple, but also TDD it, and let’s assume our order has a status attribute which holds a string.

spec/models/order_spec.rb

require 'spec_helper'

describe Order, '#cancel' do
  it 'changes the order status to cancelled'
end

Rspec uses two blocks: describe blocks and it blocks. They’re basically a way for you to identify the method and model being tested (describe), and put into english what your spec should be doing (it). When your test fails, the output will tell you Order#cancel changes the order status to cancelled is failing, i.e. it doesn’t actually change the order status to cancelled. You should have one describe block for each method on your model, and you can have many it blocks depending on how much each method is doing. In general, each it block should only test one thing. If you had a Dog model with sleep, eat, and bark methods, your related spec-file would have three describe blocks: one for eat, one for sleep, and one for bark. If eat could have three possible outcomes depending on the food passed as an argument, then the one eat describe block would have three it blocks: one to test each possible outcome.

Example:

class Dog
  def sleep
    p 'Zzz'
  end

  def bark
    p 'woof.'
  end

  def eat(food)
    if food
      p 'om nom nom'
    else
      p '...'
    end
  end
end
require 'spec_helper'

describe Dog, '#sleep' do
  it 'prints some zzzs'
end

describe Dog, '#bark' do
  it 'gives me a woof'
end

# multiple it blocks for multiple outcomes
describe Dog, '#eat' do
  it 'noms the food if there is food'

  it 'is not entertained if it is being teased'
end

Back to our Order model. We’re cancelling the order, and we have the describe and it blocks set up. Now we need the nougaty center of the it block, the actual test. Don’t fret, let’s break it down. Each test has three parts:

  • Preparation
  • Execution
  • Assertion

In preparation, we prepare everything we need to execute the function. In execution, we just call our method. In assertion, we just check to see if the result is what we expected. For our order to be cancelled, we prepare by making an order that is not already cancelled. To execute, we’ll just call cancel on it. And to assert, we’ll just check that the status is now cancelled. Easy.

Typical pattern

require 'spec_helper'

describe Model, '#method' do
  it 'does something in plain english' do
    # preparation

    # execution

    # assertion
  end
end

For our order, something like this:

require 'spec_helper'

describe Order, '#cancel' do
  it 'changes the order status to cancelled' do
    order = Order.new(status: 'complete')

    order.cancel

    expect(order.status).to eq 'cancelled'
  end
end

The only line that’s really new here is the assert. Rspec is currently trending toward expect syntax over should syntax, so let’s break that down.

All expect syntax looks like this:

expect(test_thing).to eq expected_thing

The expect and #to wrap the subject being tested, unless we use a #to_not:

expect(test_thing).to_not eq expected_thing

You may also see be_something in place of eq expected_thing as in:

expect(test_thing).to be_true
expect(test_thing).to be_even
expect(test_thing).to be_present

Basically, be_ takes a method that you would normally call on the subject.

Ruby RSpec
2.even? expect(2).to be\_even
nil.present? expect(nil).to\_not be\_present
nil expect(nil).to be\_false

The exception here is be_true/be_false which tests for the truthiness/falsiness of objects (currently).

Let’s write the code now.

app/models/order.rb

class Order < ActiveRecord::Base

  def cancel
    update_attributes(state: 'cancelled')
  end
end

Hopefully this is an easy starting point for you to ease on into TDD, but I realize production code can be more complicated. To address this, I’ll be sure to talk about stubs and mocks in my next post.

Javascript is Ruby

Ruby was my first language, and when I was introduced to javascript, I found the syntax beastly, the patterns confusing, and anonymous functions baffling (wtf is an anonymous function!?). Hopefully, this exercise will convince you, that ruby and javascript can be looked at similarly. Note: this exercise assumes some familiarity with ruby and js.

Part 1

Challenge

Simply put, your challenge is to create a hash whose keys can execute methods. Let me demonstrate with some pseudocode using a method we’re all familiar with: printing. Note: p in ruby both prints and returns the value in question, which is different behavior than both puts and print which print the value with or without a newline and return nil).

# psuedocode
> hash[:key] = p 'hello' # we store the method in the hash
> hash[:key]             # we access the key
"hello"                  # the method is executed
=> "hello"               # the value is returned

Obviously, we can’t just do hash[:key] = p 'hello', but it gets the point across of what we’re trying to achieve. Can you think of a solution?

Solution

In ruby, there are objects called procs and lambdas, which are similar to javascript’s anonymous functions. We could store a proc/lambda in the hash like so:

> hash[:hello] = -> { p 'world'}              # store the lambda
> hash[:hello]                                # access it
=> #<Proc:0x007f8531586430@(irb):3 (lambda)>  # ruby returns lambda object

This is close. It’s returning the “function”, but it isn’t calling it like our challenge would like.

There are a number of ways to accomplish this first challenge, but this is one:

class SpecialHash < Hash
  def [](key)
    super.call
  end
end

Here, I’ve made a special hash object that inherits from the standard hash, so we get all the goodies and we can still use super. super here will call the regular hash hash[:key] and we’ll get the returned proc object that we’re storing. Now, we simply call the proc to execute the method. Let’s see if it works.

> hash = SpecialHash.new
> hash[:hello] = -> { p 'world'}
> hash[:hello]
"world"
=> "world"

Sweet. We even chain them for a cool effect:

> hash['world'] = -> { p 'inception hash' }
> hash[hash[:hello]] # here, the return value from the first key is the second key
"world"
"inception hash"
=> "inception hash"

Part 2

Challenge

Let’s ensure that we can still insert data into our hash. By data, I mean non-methods. Right now, our hash explodes:

> hash[:hello] = 'world'
> hash[:hello]
NoMethodError: undefined method `call' for "world":String

Side note: if you used eval to solve the first challenge, you’ll get a different error:

# solution using eval
class SpecialHash < Hash
  def [](key)
    eval super
  end
end

> hash = SpecialHash.new
> hash[:hello] = 'p "world"'
> hash[:hello]
"world"
=> "world"
> hash[:hello] = "world"
> hash[:hello]
NameError: undefined local variable or method `world' for {:key=>"hello"}:SpecialHash

Solution

Let’s simply detect if its a method or not.

class SpecialHash < Hash
  def [](key)
    super.is_a?(Proc) ? super.call : super
  end
end

Now our hash detects if our value is a method or if it’s data. If it’s a method, it calls it/executes it; if it’s data, it returns it.

> hash[:hello] = -> { p 'world' }
> hash[:hello]
"world"
=> "world"
> hash[:hello] = 'world'
> hash[:hello]
=> "world"

Before, if we had tried to nest our hashes:

> hash[:inception_hash] = -> { SpecialHash.new }
> hash[:inception_hash][:hello] = 'world'
> hash[:inception_hash][:hello]
=> nil

Nil? Why isn’t it ‘world’? Well, in this case, a new special hash is created every time the value is accessed; therefore, none of the data store in it will persist. The bonus with our new way is that we can now nest our special hashes and have the data persist.

> hash[:nested_hash] = SpecialHash.new
> hash[:nested_hash][:hello] = -> { p 'world' }
> hash[:nested_hash][:hello]
"world"
=> "world"

Part 3

Challenge

Let’s say the method I’m storing wants to access the hash I’m storing it in and the key it’s going to be assigned to. How can I give it access to those values?

# illustrating the problem
> hash[:key] = -> { p hash; p key}
"{:key => #<Proc:0x007f8531586430@(irb):3 (lambda)> }"
# the proc can access the whole hash
":key" # the proc also knows what key it's being bound to

Solution

class SpecialHash < Hash
  def [](key)
    super.is_a?(Proc) ? super.call(self, key) : super
  end
end

What we’ve done here is to pass self, which is the hash and key, which is the key to the proc when it’s being called. So now, we can write:

> hash[:hello] = Proc.new { |entire_hash, key| p entire_hash; p key }
> hash[:hello]
{:hello=>#<Proc:0x007f8531586430@(irb):3>} # prints entire hash
:hello  # prints key

But, we have a problem.

Part 4

Final Challenge

Now, our shit breaks if our proc doesn’t take arguments :/

> hash[:hello] = -> { p 'world' }
> hash[:hello]
ArgumentError: wrong number of arguments (2 for 0)

Solution

We can overcome this by testing the waters, seeing how many arguments our proc takes, and feeding it what it wants.

class SpecialHash < Hash
  def [](key)
    return super unless super.is_a?(Proc)
    case super.arity
      when 2 then super.call(self, key)
      when 1 then super.call(self)
      else super.call
    end
  end
end

So now, it works in all cases:

> hash = SpecialHash.new
> hash[:hello] = -> { p 'world'}
> hash[:hello]
"world"
=> "world"
> hash[:hello] = 'world'
> hash[:hello]
=> "world"
> hash[:hello] = lambda{ |entire_hash| p entire_hash }
> hash[:hello]
{:key=>#<Proc:0x007f85338f0948@(irb):75 (lambda)>}
=> {:key=>#<Proc:0x007f85338f0948@(irb):3 (lambda)>}

Takeaway

The takeaway here is the similarities between javascript and ruby. In js, an object is basically a hash, and we can assign functions to that hash:

> var jsObject = {}
> jsObject.hello = function(){ console.log('hello') }
> jsObject.hello()
"hello"

Can you see the similarity to this?

> hash = {}
> hash[:hello] = -> {puts 'hello'}
> hash[:hello].call
"hello"

And how these relate:

Ruby Javascript
-> { puts 'hello' } function() { console.log('hello') }
greet = -> {
  puts 'hello'
}
greet = function(){
  console.log('hello')
}
def greet
  puts 'hello'
end
function greet(){
  console.log('hello')
}

Ruby procs are basically anonymous functions in js. If you think about it differently, you could think of ruby objects as being hashes too, hashes of key value pairs and hashes of functions, but when you call a function on a ruby object/hash, it’s as if it’s called implicitly (i.e. you don’t need parens on hash.hello() you could just call hash.hello and it knows to call the method).

Hopefully this helps somewhat with your transition from rubyland into js, and hopefully the exercises, while admittedly a wee bit convoluted, were also fun and educational.

Cheers