Developing a Rails model using BDD and RSpec, Part 1

Writing Rails testing articles seems to be quite popular at the moment; seeing as I’m often quite vocal about testing on the #caboose and #rubyonrails IRC rooms I felt it was about time I posted one of my own. I have a large series of articles on testing with Rails in the pipeline, but until that is done, here is a nice and simple tutorial for newcomers to BDD and RSpec – the first in a two-part article exploring the development of a typical Rails model, using BDD techniques and the RSpec framework. If you are interested in BDD and RSpec, or new to testing in general and want to learn how to iteratively develop a model test/spec-first, this is the article for you.

Anatomy of a typical Rails-style test

Will Rails developers please raise their hands: how many times have you written a test that looks like this:

class UserTest < Test::Unit::TestCase
  def test_create
    user = User.create(:some => 'params')
    assert user.save
  end
end

Now ask yourself how many times have you sat back and asked yourself why you are writing the above test?

If the concept of unit testing is new to you, then writing tests at all is a great first step. But its also important to have useful tests. Are your tests valuable? Are your tests acceptable?

Avoid meaningless tests

The above test is a good example of a meaningless test. Why is it meaningless? Because you aren’t testing your own code; you are testing the ActiveRecord library, which is pretty well tested already. Let’s take a look at a default Rails model:

class User < ActiveRecord::Base
end

Just those two lines of code give us a whole load of functionality, all of which is provided by the ActiveRecord library. Its fair to assume that the functionality given by those two lines of code will work. If it doesn’t then there is either something wrong with your local setup or something fundamentally wrong with ActiveRecord; in either case, your own tests are the last of your problems.

Test your own code

So if we can safely assume that the built-in ActiveRecord functionality works as advertised, what should you be testing? The simple answer: test any code that you write. Anything that gets added to your model needs test coverage. The aim of this tutorial is to place an emphasis on testing the behaviour of your code in different situations (or contexts). This is the basis of Behaviour Driven Development, the methodology that I will use in this tutorial to iteratively develop a Rails model, test-first spec-first.

For this tutorial, I will be using the excellent RSpec framework, but you could easily apply these principles to TDD using Test::Unit. Before we get started, you’ll need to install RSpec and the RSpec On Rails plugin for your current app:

~/mygreatapp/ $ sudo gem install rspec
~/mygreatapp/ $ ./script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_X_Y_Z/vendor/rspec_on_rails/vendor/plugins/rspec
~/mygreatapp/ $ ./script/generate rspec

Replace X, Y and Z in the above with the version of RSpec that you are using. If you have any problems, refer to the full instructions.

Going into RSpec in full detail is outside of the scope of this article, but it should be pretty clear what is going on – this is one of RSpec’s strengths. If you would like to read a more generic RSpec tutorial, the RSpec website has a great tutorial to get you started.

The problem

We’re in the process of writing our fantastic new Web 2.0 application, and we’ve decided that we need people to be able to create accounts and log in to the application. We don’t want to use any of the available Rails authentication plugins; we want to develop our own User model. After a quick whiteboard/CRC session, we come up with a few basic specs for our User model:

  • A user should have a username that they can log in with
  • A user should have a password between 6 and 12 characters in length
  • A user’s password should always be encrypted in the database
  • A user should have an email address
  • A user can optionally have a first name, surname and profile/description

With this in mind, we fire up our favourite text editor and start work on a new Specification. We use the generator that comes with RSpec on Rails to generate a new model, with an accompanying spec file.

$ ./script/generate rspec_model User

This will create a new user.rb file for our model, just like the normal Rails script/generate model command, but it will also create an accompanying spec file in the spec/ directory. If you open up the created user_spec.rb file, you will see a stub context ready and waiting.

Behaviour Driven Development favours the breaking up of specifications into individual “contexts”. A context is an object (or collection of objects, but generally object being specced) in a certain state. As we are going to start our specs from scratch, you can safely remove the stub context in the user_spec.rb file (don’t remove the require line at the top though!).

So what is a good starting point? I tend to favour a more generic starting context: “A user”. We can use this to specify the behaviour of a user in general.

Specifying your model in code

Let start with our first specification: a user should have a username that they can log in with. Its fair to assume that the username is required (otherwise they won’t be able to log in). So what could we specify? How about this:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "must have a username" do

  end
end

That’s not bad, but it could be better. We’ve expressed a requirement in our code but we haven’t said anything about the behaviour of a User object. What about this instead:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do

  end
end

That’s better. Not only have we expressed that our user must have a username, but we’ve also expressed what behaviour should be expected from the User model if it doesn’t have one; it should be invalid. Let’s fill this spec in, so we have a failing spec:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.username = 'someusername'
    @user.should_be_valid
  end
end

Now we need to make this pass. The first things we need is our actual User model, and a table in our database. BDD (and TDD) emphasise taking small steps, following the red, green, refactor mantra. However, due to our coupling to the database as a result of using the ActiveRecord pattern, we are going to have to make a slightly larger leap: our users table schema.

We need to write a migration for our users table, but at this stage we aren’t certain exactly what columns we need. We could write a migration every time we want to add a column but that would quickly become tedious. Instead, we’ll make a reasonable guess at our schema based on our written specs – if we get it wrong at this stage it doesn’t matter. Migrations make it easy to modify our schema in the future. Something like this should do the trick:

class AddUsersTable < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :first_name, :string
      t.column :last_name, :string
      t.column :email, :string
      t.column :description, :string
      t.column :username, :string
      t.column :encrypted_password, :string
      t.column :salt, :string
    end
  end

  def self.down
    drop_table :users
  end
end

You’ll note that we’ve made a few assumptions regarding our password columns. We already have an idea in mind about how we want to store the password – as a salted hash – so we’ve created columns for the encrypted password and salt. Now we’ve written and run our migration, and created our User model, its time to get the spec to pass:

class User < ActiveRecord::Base
  validates_presence_of :username
end

You’ll notice that we’ve not added a should statement for the error message itself. That is because we know Rails will happily provide us with the default “can’t be blank” message. Remember: only test the code that you write. In this case, we decide we do want a custom message, so lets add a spec and make it pass:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required" 
    @user.username = 'someusername'
    @user.should_be_valid
  end
end

class User < ActiveRecord::Base
  validates_presence_of :username, :message => 'is required'
end

We’ve also specified that our user must have an email address, so lets add a spec for that:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required" 
    @user.username = 'someusername'
    @user.should_be_valid
  end

  specify "should be invalid without an email" do
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required" 
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end
end

That’s simple enough to implement:

class User < ActiveRecord::Base
  validates_presence_of :username, :message => 'is required'
  validates_presence_of :email, :message => 'is required'
end

Great, we’re on a roll. But wait a minute, both of our specs are now failing. What gives? Of course, because we’ve now added two validation requirements, we need to add an email address in the first spec to make it pass and a username in the second spec to make that one pass. Hmm, it doesn’t sound very DRY, but lets go with it for now – we want our specs to pass after all!

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.email = 'joe@bloggs.com'
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required" 
    @user.username = 'someusername'
    @user.should_be_valid
  end

  specify "should be invalid without an email" do
    @user.username = 'joebloggs'
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required" 
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end
end

Phew, that was a close one. Finally, lets add the specs for the password. We know a password is required and that it has to be between 6 and 12 characters in length. Because that is actually two specifications, we’ll write two separate specs in our code. Lets start with the required field specification, as that will look similar to our above specs:

context "A user (in general)" do
  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.email = 'joe@bloggs.com'
    @user.password = 'abcdefg'
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required" 
    @user.username = 'someusername'
    @user.should_be_valid
  end

  specify "should be invalid without an email" do
    @user.username = 'joebloggs'
    @user.password = 'abcdefg'
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required" 
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end

  specify "should be invalid without a password" do
    @user.email = 'joe@bloggs.com'
    @user.username = 'joebloggs'
    @user.should_not_be_valid
    @user.password = 'abcdefg'
    @user.should_be_valid
  end
end

Now, we don’t actually have a password column in our users table, but we need somewhere to store the cleartext password before it gets encrypted. A standard Ruby instance variable will do. Here’s the code to make it pass:

class User < ActiveRecord::Base
  attr_accessor :password

  validates_presence_of :username, :message => 'is required'
  validates_presence_of :email, :message => 'is required'
  validates_presence_of :password
end

Refactoring towards cleaner, clearer specifications

Before moving on to the password length specification, lets address our duplication issue here. Its already getting tedious adding all the other required fields in each spec in order to make them pass. It is making our specs bloated, ugly and it will be a nightmare to maintain in the future if our specification changes. Let’s solve this by introducing a small helper module and a neat Hash extension:

module UserSpecHelper
  def valid_user_attributes
    { :email => 'joe@bloggs.com',
      :username => 'joebloggs',
      :password => 'abcdefg' }
  end
end

context "A user (in general)" do
  include UserSpecHelper

  setup do
    @user = User.new
  end

  specify "should be invalid without a username" do
    @user.attributes = valid_user_attributes.except(:username)
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required" 
    @user.username = 'someusername'
    @user.should_be_valid
  end

  specify "should be invalid without an email" do
    @user.attributes = valid_user_attributes.except(:email)
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required" 
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end

  specify "should be invalid without a password" do
    @user.attributes = valid_user_attributes.except(:password)
    @user.should_not_be_valid
    @user.password = 'abcdefg'
    @user.should_be_valid
  end
end

There, thats much DRYer, more expressive and easier to maintain. If our valid attributes ever change, we only need to change them in one place. However, we haven’t sacrificed readability in the name of DRY, which is very important with any tests/specs.

Finally, lets add a spec for our password length:

specify "should be invalid if password is not between 6 and 12 characters in length" do
  @user.attributes = valid_user_attributes.except(:password)
  @user.password = 'abcdefghijklm'
  @user.should_not_be_valid
  @user.password = 'abcde'
  @user.should_not_be_valid
  @user.password = 'abcdefg'
  @user.should_be_valid
end

And to make it pass:

class User < ActiveRecord::Base
  attr_accessor :password

  validates_presence_of :username, :message => 'is required'
  validates_presence_of :email, :message => 'is required'
  validates_presence_of :password
  validates_length_of :password, :in => 6..12, :allow_nil => :true
end

You’ll notice we’ve added the :allow_nil option to the length validation. This is to avoid a double validation error if we haven’t set a password – the validates_presence_of validation will already handle this and we don’t want an extra error message complaining about the length of the password as well.

There is one last refactoring that we can do at this stage. In each of our validation specs, we’ve checked that the model is invalid, then set the required value and checked that it is now valid, to ensure that the validation is working end to end. We can extract all of these checks into a single specification:

  specify "should be valid with a full set of valid attributes" do
    @user.attributes = valid_user_attributes
    @user.should_be_valid
  end

Download the full specification.

Whats next?

So far we’ve written a basic User model, with an initial schema and a validation of required attributes. We’ve covered the basics of RSpec syntax and we’ve learnt how to DRY up our specs by extracting common code into a helper module.

In the second part of this tutorial, we’ll look at password encryption and authentication. If you have any questions or feedback, do not hesitate to leave a comment; I’ll be happy to answer any queries you may have.

Return to home page | Check out my tumblelog

55 Comments on this article

1. Comment by Andrew Turner on 29 Aug 2006 at 17:08

Great introduction to rspec.

I’m curious though – what are the benefits of using rspec vs. the test/unit that comes with Rails?

2. Comment by Vincent on 29 Aug 2006 at 17:08

Luke, but does this testing make any sense? I mean, validation is a part of ActiveRecord, “which is pretty well tested already”

3. Comment by rick on 29 Aug 2006 at 17:08

I wrote a simply_bdd plugin that lets you test out the contest/specify stuff, and keep the same ruby/rails test assertions that you’re used to.

I think testing simple validations is a bit much. I definitely don’t do it that much in OSS projects :) It’s just a judgement call you have to make I suppose. Do you have the time to test every single line? Perhaps you could test the validations in the controller and knock 2 birds with one stone? Test that leaving out a ‘name’ in the field leaves the model invalid and showing the proper error messages in the HTML.

Luke: great article!

4. Comment by Luke Redpath on 29 Aug 2006 at 18:08

Vincent: the underlying functionality of the validation methods is tested in the Rails core, certainly. However we aren’t testing the validations are working per se, but we are testing that our models contain these validations. There is a key difference. “Test the code you write”. You have to add the validations yourself so they should be tested IMO.

This is a simple example of course, but when you start having more complex validations, which work with related objects, it becomes even more important to test/spec your validations.

Rick: “do I have time”? Yes, I always write my specs before I write my code. One thing that is hard to convey through a tutorial such as this is the speed with which you can write your specs/tests when you are used to working that way and have some experience with testing.

Testing tutorials often emphasise taking small steps, and its a good tip; in reality though, as you become a more experience tester you are able to take slightly bigger steps in one go, dropping back to smaller steps when things start to go awry. In a real project, I would likely run through all of those validation specs in about 20 seconds, and add the code to make them pass in one go, in about 10.

One final point: tests/specs for such basic functionality might seem trivial at this stage of the development, but as development progresses and domain models evolve, things might get broken, even by accident. If a validation enforces a critical business rule and it accidentally gets removed, you are in trouble. Rails makes validations easy to implement but never underestimate their importance. Having a well developed test/spec suite means you always have a suite of regression tests to let you know when something has broken.

Thanks for the comments though!

Andrew: regarding which is better; for practicing the BDD-style of development that I’m using in this article, I find RSpec’s syntax far mor intuitive. In real terms, both Test::Unit and RSpec can be equally effective in giving your codebase good test/spec coverage, but I find the RSpec code to be far more intention revealing, easier on the eye (no LongContextNamesAsClassNames or long_spec_names_as_method_names_with_loads_of_underscores_etc), and far more Rubyish. It also has support for mocking built straight in (though I recommend FlexMock if you do use Test::Unit). Give it a try and see which you prefer.

5. Comment by Aslak Hellesøy on 29 Aug 2006 at 18:08

Andrew,

There are several benefits.

On the non-technical level RSpec provides a language that aims to put you in the headspace that will allow you to get to step 5, 6 and 7 as described here: http://behaviour-driven.org/TDDAdoptionProfile. Of course it’s possible to get here with xUnit, but it’s harder for most people. Some people argue this is related to the Sapir-Whorf theory – the words we use change the way we think.

On the technical side there are benefits too. If you run specs with the “—dry-run—format specdoc” options, you’ll get very useful documentation of your code’s intended behaviour.

RSpec has built-in diffing, which is useful for big string comparisons. Just use the—diff option.

If you’re using RSpec with Rails, the rails_spec_runner will let you execute specs fast. It keeps Rails running in the background so you don’t have to restart it every time.

RSpec has built-in mock objects, RCov support, a Rake task and other goodies.

Thanks Luke, for writing this great article!

6. Comment by Meekish on 29 Aug 2006 at 23:08

Luke, I have been anticipating this article since you were talking about it in the RailsDay campfire. Thanks for taking the time. This a great help in grasping BDD and rSpec!

7. Comment by asherp on 30 Aug 2006 at 00:08

Mispelling in the paragraph that ends with “should do the trick” t.coulmn :last_name, :string

8. Comment by asherp on 30 Aug 2006 at 00:08

err…

Mispelling AFTER the paragraph that ends with “should do the trick” in the code snippet. t.coulmn :last_name, :string

9. Comment by John Nunemaker on 30 Aug 2006 at 03:08

So do you personally use only rSpec? If so, for how long?

10. Comment by Guillaume Maury on 30 Aug 2006 at 06:08

Thanks for this article…. I must say I really like the syntax of rSpec… I’ll have to look at it more…. and “—dry-run—format specdoc” is a rather big point in favor of rSpec….

11. Comment by Guillaume on 30 Aug 2006 at 07:08

I see that for the unobstrusive javascript plugin, you decided to use unit test instead of rspec even though the names of the methods show clearly that you have developed them using BDD Why didn’t you just do the test with rspec ? to limit the dependency needed for users of your plugin?

12. Comment by Peter Arrenbrecht on 30 Aug 2006 at 07:08

Hmm. I still think there is something missing with BDD. In a word: “explanation”. If BDD is just about implementing given specs in code, then why repeat yourself in natural language? If it’s about developing the specs on-the-fly, then it is too terse. See my longer post at

http://peomeint.blogspot.com/2006/07/rspec-and-bdd-something-crucial.html

for details, and for my own take on developing good APIs and tests:

http://peomeint.blogspot.com/2006/08/beyond-tdd-documentation-driven.html

13. Comment by Martin on 30 Aug 2006 at 09:08

Luke, excellent article. I’ve been experimenting with RSpec since I chatted to you at the last LRUG meeting. Before I had that same feeling of “why I am I testing already tested code?!”.

Its a much more natural way using RSPEC. I’ve stuck with this method of testing for all of my more recent projects since I got into it.

What would be complimentary to this article is a further reading list / examples of Ruby (on Rails) project source that include RSPEC tests. RAPT is one and Scruffy also has some http://scruffy.rubyforge.org/. Any more?

Looking forward to the next installment Luke!

14. Comment by Luke Redpath on 30 Aug 2006 at 09:08

John: I currently use both, although I lean towards RSpec for personal projects. RSpec on Rails is nearly there and here at Agile Evolved we are looking at moving towards using RSpec on our other projects.

Guillaume: its only in the past couple of months that RSpec has really started to mature for Rails projects. I was more comfortable using Test::Unit when I started work on the UJS plugin. Not just that, but Dan Webb who works on the plugin with me is more familiar with Test::Unit (although he’s planning on learning RSpec). I’ve actually been maintaining an interested watch on RSpec for about 6 months and started playing with it around then.

It might interest you to know that we are using RSpec for the Rails Plugin Repository. I’ve also used RSpec for my RubySlim API.

On a small note, I noticed I missed out a small but crucial element in my article regarding the password specs: an instance attribute for the password, as it isn’t a column in the DB. I’ve updated the article accordingly.

Thanks for your comments.

15. Comment by Luke Redpath on 30 Aug 2006 at 09:08

Martin, thanks for your comments. I’ll be sure to include some links at the end of Part 2 (which I’ll write today or tomorrow, time permitting).

16. Comment by Jan Wikholm on 30 Aug 2006 at 12:08

Wow. Excellent writing Luke!

You certainly have used the Force here ;)

I’ll definitely try out rSpec thanks to this enlightening example.

Cheers!

17. Comment by Daniel Lucraft on 30 Aug 2006 at 14:08

Great article.

When can we expect part II?

18. Comment by Daniel Lucraft on 30 Aug 2006 at 14:08

Duh! Answer to my question is right there two posts up. Soz.

Well anyway I’m looking forward to it.

19. Comment by mly on 30 Aug 2006 at 20:08

Great article, Luke. You explained the general concepts and ideas of BDD much better than anyone else I’ve seen attempting it.

The hash trick you linked to looks derived from something I posted to the caboose blog a while ago. The pastie is a fair bit cleaner though.

20. Comment by Luke Redpath on 30 Aug 2006 at 20:08

mly – thanks for the comments. Yeah, it looks similar; I confess I don’t know the origin of the Hash trick. I got it from James Adam and he can’t remember where he got it from either.

If anybody out there did actually write the code I linked to, do speak up!

21. Comment by Pat on 31 Aug 2006 at 02:08

Nice tutorial.

I don’t think you should have the should_be_valid calls. You’ve written the specification that matters, and made it work. Specifying what’s required for a valid model would be a different context altogether. As you saw, combining this into the same contet led to some ugliness. You ended up having to set the email address, even though it didn’t matter for that particular specification.

This may be a matter of preference, but I think it would make more sense to break all the validation into different contexts.

context "A user without a username" def setup @user = User.new @user.save end specify "should be invalid" @user.should_be_invalid end specify "should provide an error message" @user.errors.on(:username).should_equal "is required" end end

There is a bit of a “hack” in there with calling save in setup (so that the errors are set), but it still feels a lot cleaner than setting unnecessary attributes in your specs. Especially once you get a lot.

Once you see this duplication, you could do some fun metaspec stuff :) Maybe end up with code like

user_with_blank_username_has_error “is required”

which would create the spec you need.

This validation is a pretty simple example, but hopefully you can see why I think your contexts should be more specific than general. Once you get into more complex stuff it’ll be even more apparent.

22. Comment by Luke Redpath on 31 Aug 2006 at 09:08

Pat, I’m afraid I have to disagree. Splitting it up into separate contexts would be far more verbose than I’d like. The validation rules apply to all users which is why I feel that they are appropriate under a single “general” context. Yes it led to some ugliness to begin with but after refactoring the should_be_valids into a single two line spec, I was quite happy with how clean it is.

Also, you can just call valid? to populate the errors without having to call save. As I’ll go into in the second part, I like to avoid hitting the database wherever possible in my unit tests (in an ideal world unit tests wouldn’t touch the DB at all).

23. Comment by Pat on 31 Aug 2006 at 15:08

I don’t believe that tests need to be DRY at all. So I’m not really worried about the duplication in my tests. I also don’t think that my way is too verbose at all – each context and specification is very clear and short.

You’re including your context in the specification itself. It’s pretty easy to see because you mention attributes in the spec names:

specify “should be invalid without a username” specify “should be invalid without an email”

Your specs should specify behavior. You’re specifying behavior under a certain condition…context.

You may have already read it, but in case you haven’t, Dave Astels has a very good article.

24. Comment by Luke Redpath on 31 Aug 2006 at 15:08

Pat; I’m not disagreeing that splitting each validation up into individual contexts would be technically more correct. I’m simply stating a preference to keep my spec files lean and only introducing new contexts where I feel its absolutely necessary.

If I feel a context would only have a single behaviour (which I do at this stage with the validations) then I’m happy to keep it in the generic context as long as it still makes sense (and “A user in general should be invalid without a username” makes sense to me).

If further down the line I feel it makes sense to extract a spec from one context into one of its own, then I will do. In fact, the specs for the password, currently sitting in the generic context, will have other behaviours besides validations so they will end up being extracted into new contexts. This is something I’ll be exploring in the next part of my article.

Regarding whether or not tests should be DRY: I believe that all code should be as DRY as possible, except, in the case of tests/specs, where it sacrifices readability. If I can make things DRYer and keep things readable and clear, then I will do so.

Thanks for your comments though, its good to see discussion.

25. Comment by Chris Anderson on 02 Sep 2006 at 16:09

My LAME Mp3 Encoder wrapper is packaged with a set of RSpecs. This was just a quick hack I wrote on the Argon Express, and it shells out to the command line lame tool, but hey, it’s got some rad specs.

26. Comment by Russell Tracey on 03 Sep 2006 at 19:09

Following this tutorial along I was getting the following error when running the test with spec spec/models/user_spec.rb -f s

NoMethodError in ‘A user (in general) should be invalid without a username’ You have a nil object when you didn’t expect it! You might have expected an instance of Array. The error occured while evaluating nil.- ./spec/models/../spec_helper.rb:24:in `teardown’

spec_helper:24 is just calling super. Seems to occur when you define a setup method but not a teardown method in a context. Am i using an outdated version of Test::Unit::TestCase (one shipped with Ruby 1.8.4) or maybe something else?

RSpec 0.6.3 Rails 1.1.6 Ruby 1.8.4

27. Comment by Russell Tracey on 03 Sep 2006 at 19:09

Sorry about the formating on my last post Mepthisto seems to of got confused with my use of dashes to seperate the code of the text.

28. Comment by Jacob Stetser on 04 Sep 2006 at 01:09

That Hash extension looks like something I wrote for Zaadz, but cleaned up a little.. My name should be linked to the blog post I put up about it.

Kinda odd to be looking at a cleaned-up version of my own code, but the rdoc comments cinch it!

Glad it’s been of use! Was the pastie the way you found it? Or did you make those changes?

Great article!

29. Comment by Jake Stetser on 04 Sep 2006 at 02:09

Also, I’m having an odd problem with rspec and fixtures.. My installation of them won’t load fixtures at all.. very strange!

I’ve worked around it by creating new objects within the spec, but that could get to be a pain really quick ;)

30. Comment by bruno on 04 Sep 2006 at 03:09

I’m having the same problem as Russel – error on nil.- in the teardown method. Looks like ActiveRecord’s teardown method is getting called when there’s no transaction to decrement: /vendor/rails/activerecord/lib/active_record/transactions.rb:107:in `decrement_open_transactions’

I’m on EdgeRails with Ruby 1.8.4.

Any thoughts?

31. Comment by Luke Redpath on 04 Sep 2006 at 07:09

It sounds like a strange problem I was having; I fixed it by turning off transactional fixtures (which is fine for me as I don’t use Rails fixtures). Does that help?

32. Comment by Russell Tracey on 04 Sep 2006 at 19:09

Yep fixed here.

33. Comment by Josh on 01 Oct 2006 at 15:10

Interesting article, thanks! Couldn’t find the 2nd part though… is it already online? :-)

34. Comment by s.ross on 08 Oct 2006 at 00:10

In a shared hosting environment (I know, bad, bad…), I am not able to control which gems are present. Given the uncertainty of rSpec’s availability on the deployment server, do you have any recommendations regarding deployment?

My current approach is to put code in my after_update_code Capistrano tasks to make my Rakefile and rake tasks look as though it were a generic Test::Unit project.

If you’re wondering why having a Rakefile that works on a production server is important, I think migrations are the most straightforward example where rake simply has to work.

Thanks for the article.

35. Comment by s.ross on 08 Oct 2006 at 06:10

Another quick note. When writing a custom setup function, it seems you have to call super to allow the base class to properly create the transaction. Teardown will try to roll back that transaction and will fail if you don’t. (Assuming you’re using transactional fixtures.)

36. Comment by DougV on 25 Oct 2006 at 04:10

I’m not sure if rspec has changed since this expample was posted, but to get this to work I have to use: setup do

and not def setup

Very good article though…

37. Comment by Luke Redpath on 20 Nov 2006 at 10:11

Thanks for your comments everybody – RSpec has gone through a few changes since I wrote this article and I’ll cover these, plus any errors in the second part of the article which I’m hoping to do soon! (sorry for the delay – its been a busy few months!).

38. Comment by Levent on 21 Nov 2006 at 12:11

Luke, except doesn’t seem to be a valid method on the Hash instance. Is this something you wrote? It’s not in the ruby standard lib I don’t think?

undefined method `except’ for #<hash:0×37a31f8>

39. Comment by Luke Redpath on 21 Nov 2006 at 13:11

Levent – yes you’ll need the hash extension that I linked to – you can just drop it into your spec_helper.rb file. Here it is again

40. Comment by Cameron on 21 Nov 2006 at 21:11

Excellent to hear you’re working on a follow-up article Luke, just about to start delving into RSpec so an updated walk-through would undoubtedly prove useful!

41. Comment by weepie on 29 Nov 2006 at 09:11

Hey great article Luke

—when’s part 2 coming up :)

42. Comment by Gerry Power on 20 Dec 2006 at 05:12

The correct install path for version 0.7.5 of the plugin is: script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_5//rspec_on_rails/vendor/plugins/rspec_on_rails

This path is different then the one specified above.

Thanks for the great write-up!

43. Comment by Frank Quednau on 29 Dec 2006 at 22:12

Greetings, and excuse my noob odour! I have been going through your tutorial, writing it myself to get my environment going and to get used to the syntax. It all seems to work pretty well, except for the fact that I get this somewhat funny failure: ‘A user should be invalid without email’ FAILED “is required” should equal “is required” ./spec/models/user_spec.rb:33:

er…right…

The line in question: @user.errors.on(:email).should_equal “is required”

The line in the User model that satisfies the check is: validates_presence_of :email, :message => ‘is required’

With the following line I get this to work @user.errors.on(:email).should_match(/is required/)

So, what gives? Can you not compare a string to another with Object.equal? Am I missing something?

Otherwise, thanks so much for this tutorial, gives me an idea what the RoR community is up to these days :)

44. Comment by Jason on 31 Jan 2007 at 10:01

As of v0.7 of RSpec there have been changes to equality. Instead of

@user.errors.on(:username).should_equal "is required" 

Try

@user.errors.on(:username).should_eql "is required" 

45. Comment by Vish K on 02 Feb 2007 at 18:02

@luke In the Hash Extension pastie , the dup in the only() method seems superfluous since Hash#reject does a dup by itself.

46. Comment by mitjok on 08 Feb 2007 at 12:02

for rspec 0.7.5.1 used different url as opposite to provided on the http://rspec.rubyforge.org: ./script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_5_1/rspec_on_rails/vendor/plugins/rspec_on_rails on edge rails. Rails 1.1.6 installed as gem.

47. Comment by Chad Humphries on 21 Feb 2007 at 01:02

You might want to add something like:

@user.errors.size.should == 1 to confirm that no extra errors are being masked during the “should be invalid without” block.

48. Comment by Abhilash on 23 Feb 2007 at 03:02

how to change the core Rails validation error messages using plugins? Pls give in details.

49. Comment by Matt on 03 Mar 2007 at 19:03

So when is part 2 coming along?

50. Comment by Sonu Chhajed on 09 Jun 2007 at 10:06

Great introduction to rspec.

I’m curious though – what are the benefits of using rspec vs. the test/unit that comes with Rails?I m waiting 4 d second part…

51. Comment by andy on 11 Jun 2007 at 06:06

great article

but i prefer to Test:Unit

52. Comment by Nancy on 02 Aug 2007 at 02:08

I don’t understand: what are the benefits of using rspec? Only faster?

53. Comment by Tony Martin on 08 Aug 2007 at 08:08

Not sure if you intended it that way, but your download full spec file did not include the email length validation spec.

54. Comment by Cody on 16 Aug 2007 at 20:08

I’m trying to get this working on RSpec 1.0.8. Some of the syntax had to be modified and it worked except for the spec checking the validates_presense_of checking for the correct message. It doesn’t seem to matter what I do, it passes the test.

55. Comment by Zubin on 18 Aug 2007 at 13:08

In the examples I’ve seen, the words are a bit different, ie “describe” instead of “context” and “before” instead of “setup”. Has there been a change in rspec or are these synonymous? eg:

describe User, “when created” do before(:each) do @user = User.new end end

it "should be valid" do
  @user.should be_valid
end

Return to home page | Check out my tumblelog

Commenting on this article is now closed