---
title: 'Back to Basics: Writing Unit Tests First'
teaser: |
  Step-by-step instructions for learning Test-Driven Development (TDD) in Ruby.
  There's nothing to fear!
  It's fun.
tags: web,back to basics,ruby,rails,testing
author: Britt Ballard
published_on: 2014-04-07
---

The value of unit tests and <abbr title="Test Driven Development">TDD</abbr> are
well documented. Unfortunately, it's still daunting to start practicing it.
Here's a primer.

## Our desired code

We need to write a method that:

* takes a string as an argument
* reverses the string
* saves the string to a file on the hard drive
* returns the reversed string

## Our tool and techniques

We'll use [RSpec](https://relishapp.com/rspec) to test our method's output.
We'll use [red/green/refactor](http://vimeo.com/43734265) methodology. We'll
write [unit tests](http://www.extremeprogramming.org/rules/unittests.html) that
are [fast, isolated, repeatable, self-verifying, and
timely](http://pragprog.com/magazines/2012-01/unit-tests-are-first).

## Starting with desired end state

What's a good first test we can write? We know that given a particular input (a
string "example string") should product a given output (the string "gnirts
elpmaxe"):

```ruby
describe StringChanger do
  it 'reverses strings' do
    string_changer = StringChanger.new

    reversed_string = string_changer.reverse_and_save('example string')

    expect(reversed_string).to eq 'gnirts elpmaxe'
  end
end
```

Now let's run our test. We'll use the documentation format, which is more
verbose, but for the sake of this example will give us more information about
the tests:

    % rspec string_changer_spec.rb --format documentation

We get the following output:

    string_changer_spec.rb:3:in `<top (required)>':
    uninitialized constant StringChanger (NameError)

The test is telling us to create a `StringChanger` class. Let's write it:

```ruby
class StringChanger
end
```

When we run our test, we get this output:

    StringChanger
      reverses strings (FAILED - 1)

    Failures:

      1) StringChanger reverses strings
         Failure/Error: reversed_string = string_changer.reverse_and_save('example string')
         NoMethodError:
           undefined method `reverse_and_save'
           for #<StringChanger:0x007fafe5c66ff0>
         # ./string_changer_spec.rb:8:in `block (2 levels) in <top (required)>'

    Finished in 0.00032 seconds
    1 example, 1 failure

    Failed examples:

    rspec ./string_changer_spec.rb:5 # StringReverAndSave reverses strings

Our test is telling us we need to write a method called `reverse_and_save`.
Let's write it:

```ruby
class StringChanger
  def reverse_and_save(string_to_reverse)
  end
end
```

We run our test again:

    StringChanger
      reverses strings (FAILED - 1)

    Failures:

      1) StringChanger reverses strings
         Failure/Error: expect(reversed_string).to eq 'gnirts elpmaxe'

           expected: "gnirts elpmaxe"
                got: nil

           (compared using ==)
         # ./string_changer_spec.rb:10:in `block (2 levels) in <top (required)>'

    Finished in 0.00301 seconds
    1 example, 1 failure

    Failed examples:

    rspec ./string_changer_spec.rb:5 # StringChanger reverses strings

## The simplest thing that could work

Our test is telling us our method's logic does match expectations. Let's make
it pass:

```ruby
class StringChanger
  def reverse_and_save(string_to_reverse)
    'gnirts elpmaxe'
  end
end
```

We run our test:

    StringChanger
      reverses strings

    Finished in 0.00079 seconds
    1 example, 0 failures

## Refactoring

We have a passing test, but there is a smell we can refactor out. Currently we
are returning a hard-coded value. This will not work. Let's do a
little refactor and then depend on our tests to tell us if things are still
good:

```ruby
class StringChanger
  def reverse_and_save(string_to_reverse)
    string_to_reverse.reverse
  end
end
```

We run our test:

    StringChanger
      reverses strings

    Finished in 0.00079 seconds
    1 example, 0 failures

Our test still passes but our method is not fully functional until we write our
reversed string to a file.

## Stubbing dependencies such as the file system

We're writing a unit test, which should be isolated. We don't want to create a
new file every time we run our test, so we stub it out.

The [RSpec Mocks](https://relishapp.com/rspec/rspec-mocks/docs) library gives
us the ability to send the `stub` message to an object and replace the method
we're stubbing with a dummy method that doesn't do anything:

```ruby
it 'saves string to the file system' do
  string_changer = StringChanger.new
  File.stub(:write)

  string_changer.reverse_and_save('example string')

  expect(File).
    to have_received(:write).
    with('example_file', 'gnirts elpmaxe').
    once
end
```

We expect the `File` object to receive the message `write` one time with the
arguments `'example_file'` and our reversed string by using the stub's ability
to report on the messages it's received. This is called
[white-box testing](http://en.wikipedia.org/wiki/White-box_testing).

We run our test:

    StringChanger
      reverses strings
      saves string to the file system (FAILED - 1)

    Failures:

      1) StringChanger saves string to the file system
         Failure/Error: expect(File).to have_received(:write).with('example_file', 'gnirts elpmaxe').once
           (<File (class)>).write("example_file", "gnirts elpmaxe")
               expected: 1 time with arguments: ("example_file", "gnirts elpmaxe")
               received: 0 times with arguments: ("example_file", "gnirts elpmaxe")
         # ./string_changer_spec.rb:19:in `block (2 levels) in <top (required)>'

    Finished in 0.00106 seconds
    2 examples, 1 failure

    Failed examples:

    rspec ./string_changer_spec.rb:13 # StringChanger saves string to the file system

Our test is telling us to invoke the `File` object's `write` method. Let's
write it:

```ruby
class StringChanger
  def reverse_and_save(string_to_reverse)
    File.write('example_file', string_to_reverse.reverse)
  end
end
```

We run our test:

    StringChanger
      reverses strings (FAILED - 1)
      saves string to the file system

    Failures:

      1) StringChanger reverses strings
         Failure/Error: expect(reversed_string).to eq 'gnirts elpmaxe'

           expected: "gnirts elpmaxe"
                got: 14

           (compared using ==)
         # ./string_changer_spec.rb:10:in `block (2 levels) in <top (required)>'

    Finished in 0.00136 seconds
    2 examples, 1 failure

    Failed examples:

    rspec ./string_changer_spec.rb:5 # StringChanger reverses strings

We made our new test pass but broke our old test because we are no longer
returning the reversed string. This shows the benefit of old tests helping
guard against regressions in our code.

Let's fix our original test:

```ruby
class StringChanger
  def reverse_and_save(string_to_reverse)
    string_to_reverse.reverse.tap do |reversed_string|
      File.write('example_file', reversed_string)
    end
  end
end
```

We run our test:

    StringChanger
      reverses strings
      saves string to the file system

    Finished in 0.0011 seconds
    2 examples, 0 failures

We now have two passing tests and a fully functional method. We did it
writing the tests first. This is a simple example, but by following this
process, we can conquer any sized programming task. Writing the tests first
will also help us write testable code and help us keep our methods small.

## What's next

If you found this useful, you might also enjoy:

* [Test Driven Development: By Example](http://www.amazon.com/Test-Driven-Development-By-Example/dp/0321146530)
