Arun Sharma
Arun Sharma

Reputation: 281

Model testing with rspec

I have a piece of code that I want to test:

class User < ActiveRecord::Base

 has_many :servers, :dependent=> :destroy

end

Please give an example of RSpec code that tests the association code.

Upvotes: 5

Views: 4367

Answers (6)

Erwin Schens
Erwin Schens

Reputation: 454

For simple spec examples/generations i would recommend using the gem regressor:

https://github.com/ndea/regressor

This gem will generate some simple specs for your model based on relations/validations/database/callbacks on which you can orientate your future specs.

Upvotes: 0

Peter P.
Peter P.

Reputation: 3515

I present two approaches: the quick and dirty, and the D.R.Y and clean :)

The quick and dirty:

context "when testing dependent: destroy" do
  before do
    @user = User.create
    @servers = [Server.create]
    @user.servers = @servers
  end 

  it "should destroy all of the associated servers" do
    @servers.each{|s| Server.exists?(s.id).should be_false}
  end
end

To do this in a DRY and reusable fashion, with only a dependence on rspec itself, you need a custom matcher for HasMany. The high level spec will look like this:

Subject { User.new }
it { should have_many :tasks, dependent: :destroy }

Then the matcher will look as follows:

RSpec::Matchers.define :have_many do |_association, opts|
  if opts and opts[:dependent]

    match do |model|
      associations(model, :has_many, opts).any? { |a| a == _association }
    end

    failure_message_for_should do |model|
      error(
        :expected => [ "%s to have many %s and to have dependent => :destroy %s", model, _association, opts[:dependent] ],
        :actual   => [ "%s has many %s ", model, associations(model, :has_many) ]
      )
    end

  else

    match do |model|
      associations(model, :has_many).any? { |a| a == _association }
    end

    failure_message_for_should do |model|
      error(
        :expected => [ "%s to have many %s", model, _association ],
        :actual   => [ "%s has many %s", model, associations(model, :has_many) ]
      )
    end

  end
end

You will need this rspec helper module: http://pastie.org/6356719

I like the first approach even though its dirty because it goes to the database and uses the full rails stack...but someone people think thats too coupled. The second approach is more dry but requires a helper that just introspects onto the association itself and may not cover other problems that may screw up the deletion of associated objects...

Upvotes: 1

ka8725
ka8725

Reputation: 2918

For me this variant works well:

describe User do
  it { should have_many(:servers).dependent(:destroy) }
end

Upvotes: 8

Here I use Factory girl:

it "should be destroyed when user is destroyed" do
  @user.servers << @server
  @user.destroy
  Server.all.should_not include(@server)
end

But you can also create a new user in the test like this:

it "should be destroyed when user is destroyed" do
  user = User.create(:name => "Julia")
  user.servers << Server.create(:server_name => "Julias server")
  user.destroy
  Server.all.should_not include("Julias server")
end

Upvotes: 1

Nerian
Nerian

Reputation: 16197

While answering your current question might help you right now, what you really need is to understand Testing.

You want to write a test for a piece of code that you have already there. With a TDD methodology, you will do the opposite: First you write the test, then you write the code that makes that test pass.

I will recommend that you buy The RSpec Book, by the Pragmatic Programmers. It will explain you very clearly what you should be doing and how. Really good book, worth every penny.

Another answer says that you should be using Shoulda. While that is a good practice, try to do it first by yourself. Once you understand what you should do, then you can use an abstraction like Shoulda.

The other answer uses a Factory. While that is a good practice, I would recommend that you first use the RSpec built-in tools, like Mock objects and stub methods. Once you understand how these two operates, then you can use an abstraction like Factories. By the way, Fabrication is simpler.

Upvotes: 4

JamesAlmond
JamesAlmond

Reputation: 181

One way of testing associations in RSpec is using shoulda. Shoulda is a collection of helpers that make common repetitive tests quick to write whilst being understandable. To test your model, make sure the shoulda gem is installed and required. Then you can test your association with something like the following:

describe User do
  it {should have_many :servers, :dependent => :destroy}
end

Upvotes: 0

Related Questions