Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I've never been really convinced of the advantages that you get by switching over to RSpec from Test::Unit in Ruby on Rails (despite reading from time to time about RSpec).

What is it about RSpec that most Rails project seems to be using it?

(some code examples clearly indicating advantages of one over the other would be much appreciated)

share|improve this question
4  
Only 1 year later and I look with disgust at Test::Unit... RSpec FTW! –  Paweł Gościcki Jan 20 '12 at 22:05

2 Answers 2

up vote 7 down vote accepted

It's largely a matter of taste, and most testing tools worth their salt support both. My personal preference is for RSpec over Test::Unit because a) the output and layout of the tests focusses on what the object under test is supposed to do (as opposed to what the code is) and b) saying 'X should Y' makes more sense to me than 'assert that X predicate Y'.

To give you some context for the points above, here's a (pretty contrived) comparison of the output/source code of two functionally equivalent unit tests, one written using RSpec and the other using Test::Unit.

Code under test

class DeadError < StandardError; end

class Dog
  def bark
    raise DeadError.new "Can't bark when dead" if @dead
    "woof"
  end

  def die
    @dead = true
  end
end

Test::Unit

 require 'test/unit'
  require 'dog'

  class DogTest < Test::Unit::TestCase
    def setup
      @dog = Dog.new
    end

    def test_barks
      assert_equal "woof", @dog.bark    
    end

    def test_doesnt_bark_when_dead
      @dog.die
      assert_raises DeadError do
        @dog.bark
      end
    end
  end

RSpec

require 'rspec'
require 'dog'

describe Dog do
  before(:all) do
    @dog = Dog.new
  end

  context "when alive" do
    it "barks" do
      @dog.bark.should == "woof"
    end
  end

  context "when dead" do
    before do
      @dog.die
    end

    it "raises an error when asked to bark" do
      lambda { @dog.bark }.should raise_error(DeadError)
    end
  end
end

Test::Unit output (as full as I could make it)

Ξ code/examples → ruby dog_test.rb --verbose
Loaded suite dog_test
Started
test_barks(DogTest): .
test_doesnt_bark_when_dead(DogTest): .

Finished in 0.004937 seconds.

RSpec output (documentation formatter)

Ξ code/examples → rspec -fd dog_spec.rb 

Dog
  when alive
    barks
  when dead
    raises an error when asked to bark

Finished in 0.00224 seconds
2 examples, 0 failures

2 tests, 2 assertions, 0 failures, 0 errors

P.S. I think Berin (previous responder) is conflating the roles of Cucumber (which grew out of the RSpec project but is independent) and RSpec. Cucumber is a tool for automated acceptance testing in a BDD style, whereas RSpec is a code library for testing which can be, and is, used at unit, integration and functional levels. Hence using RSpec doesn't preclude unit testing - it's just that you call your unit tests 'specs'.

share|improve this answer

Lately it seems that Cucumber has been getting more support in the Rails community. Here's the nutshell argument I heard from an interview with RSpec's lead (from an old Ruby on Rails Podcast).

In the agile community (at the time) there was a big push for Test Driven Development with the emphasis on testing first to prove you had to change something. There was something philisophically wrong with that. In essence, testing is a way to verify that your implementation is correct, so how do you think differently about the driving design? RSpec's lead surmised that you specify first, and wouldn't it be great if the specification also checked the results of your implementation?

The simple renaming assert to should helps to focus the mind in the correct way to write specifications, and think about design. However, that's only part of the equation.

More importantly, since many TDD beleivers were claiming that the tests documented the design, most documentation was poor at best. Tests are fairly opaque to non-developers unfamiliar with the language. Even then, the design isn't immediately apparent from reading most tests.

So as a secondary goal, RSpec devised a way to generate written documentation from the specifications. It's this written specification that gives RSpec an edge over simple Test::Unit for driving the design of an application. Why write something more than once when you can document and verify design consistency at the same time?

My understanding is that Cucumber, which had the benefit of coming later and learning lessons from RSpec, does a better job of this secondary goal without losing the primary goal (ability to test the implementation from the specification). Cucumber does this with a fairly English like API that non-programmers can reasonably grok. They might need some help from programmers to fill in some definitions of new verification methods, but it's designed to be extensible.

Bottom line is that RSpec/Cucumber shouldn't replace unit testing altogether. They may be better suited to documenting and verifying your design--but there are several implementation tests that still need to be done, particularly with subtle bugs that derive from the implementation and not the design of the application. It does reduce the number of tests you have to write though.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.