Rspec approach to test model


#1

Hi,

I am quite new to Rspec. I want to use Rspec to test my existing Code. I
start from Models (Unit Testing). Here i want your help on a issue.

Here is model/user_spec.rb

describe User do
before(:each) do
@user=User.new
@user.id=‘2’
@user.email=‘removed_email_address@domain.invalid’
@user.password=‘1234’
@user.crypted_password= ‘21066966a0578362e2115f3561bd4f144520ccd2’
@user.salt= ‘574f09d3ae2473105567eab77ba9d3ae08ed40df’
@user.remember_token= ‘’
@user.remember_token_expires_at= ‘’
@user.name= ‘kaleem’
@user.company_id=‘2’
@user.title= ‘’
@user.active= ‘1’
@user.reset_password_token= ‘’
end

it “should authenticate with valid email,password and approved
company” do
User.authenticate(@user.email, @user.password).should_not be_nil
end

it “should NOT authenticate with INvalid email” do
@user.email=nil
User.authenticate(@user.email, @user.password).should be_nil
end

it “should NOT authenticate with INvalid password” do
@user.password=nil
User.authenticate(@user.email, @user.password).should be_nil
end

it “should remember me” do
@user.remember_me
@user.remember_token.should_not be_nil
@user.remember_token_expires_at.should_not be_nil
end

it “Remember Token time should be less than Token expires time” do
@user.remember_token?.should be_true
end

it “should forget me” do
@user.forget_me
@user.remember_token.should be_nil
@user.remember_token_expires_at.should be_nil
end
end

Now Questions:

  1. is my approach or way of writing specs is right???

  2. How can i make specs for such a model action

    def activate!
    self.update_attribute(:active, true)
    end

Thank you :slight_smile:


#2

On 11/02/2009, at 8:51 AM, Kaleem U. wrote:

@user=User.new
@user.active= ‘1’
User.authenticate(@user.email, @user.password).should be_nil
@user.remember_token_expires_at.should_not be_nil
end
Thank you :slight_smile:
Hi Kaleem. All of that looks pretty good so far, though you shouldn’t
be setting the “id” attribute of an AR model:
@user.id = ‘2’

I suggest using the following format for creating your User object:

before(:each) do
@user = User.new(
:email => ‘removed_email_address@domain.invalid’,
:password => ‘1234’,
# …etc…
)
end

To spec your #activate! , why not do something like this?:

it ‘should activate the user’ do
@user.active.should be_false
@user.activate!
@user.active.should be_true
end

Cheers,
Nick


#3

On Feb 11, 2009, at 8:51 AM, Kaleem U. wrote:

@user=User.new
@user.active= ‘1’
@user.reset_password_token= ‘’
end

You could benefit from a factory / data builder. See
FixtureReplacement, Fixjour, Factory girl, one of the many others out
there which would build this stuff once for you.

end
end
end

Now Questions:

  1. is my approach or way of writing specs is right???

Remember: there is no right way of doing things. Does it work for you
(and your team)? Is it clear?

  1. How can i make specs for such a model action

def activate!
self.update_attribute(:active, true)
end

spec1:
@user.activate!
@user.should be_active

spec 2:
@user.should_not be_active

Scott


#4

Scott T. wrote:

On Feb 11, 2009, at 8:51 AM, Kaleem U. wrote:

@user=User.new
@user.active= ‘1’
@user.reset_password_token= ‘’
end

You could benefit from a factory / data builder. See
FixtureReplacement, Fixjour, Factory girl, one of the many others out
there which would build this stuff once for you.

Kaleem,

I recently installed nakajima-acts_as_fu (0.0.3) gem. This provides a
rather painless way of specifying ActiveRecord model schemata on the fly
in your specifications. You might find it helpful to look into this.

You will discover that some people favour mocks and stubs when dealing
with AR models in tests/specifications and some favour hitting the
database. Once you decide on your own preference in this matter the
choice of approach is somewhat simplified.


#5

Nick H. wrote:

To spec your #activate! , why not do something like this?:

it ‘should activate the user’ do
@user.active.should be_false
@user.activate!
@user.active.should be_true
end

Thanks for your reply Hoffman :slight_smile:

I did the same but it gives error like “#23000Duplicate entry
'removed_email_address@domain.invalid Insert INTO (…) Values(…)”.
It should do update not Insert.
But when i try call it with a new instance of user (with no values
assigned to any user attribute) then it works.
i.e.
it “should activate the user” do
@user1=User.new
@user1.activate!
end
This works.

I think i have to erase all the User attributes from “before (:each)”
and only assigned it in Examples which required it. am i right ?

Thanks


#6

James B. wrote:

I recently installed nakajima-acts_as_fu (0.0.3) gem. This provides a
rather painless way of specifying ActiveRecord model schemata on the fly
in your specifications. You might find it helpful to look into this.

You will discover that some people favour mocks and stubs when dealing
with AR models in tests/specifications and some favour hitting the
database. Once you decide on your own preference in this matter the
choice of approach is somewhat simplified.

Hi james byrne,

Good to know that.I will surely look for this gem…

Thanks :slight_smile:


#7

Scott T. wrote:

On Feb 11, 2009, at 8:51 AM, Kaleem U. wrote:

@user=User.new
@user.active= ‘1’
@user.reset_password_token= ‘’
end

You could benefit from a factory / data builder. See
FixtureReplacement, Fixjour, Factory girl, one of the many others out
there which would build this stuff once for you.

Hi Scott T.,

I am in need of such data builders.

So nice of you… :slight_smile:


#8

Kaleem U. wrote:

Scott T. wrote:

You could benefit from a factory / data builder. See
FixtureReplacement, Fixjour, Factory girl, one of the many others out
there which would build this stuff once for you.

I am in need of such data builders.

I recently thumped the square peg of FixtureDependencies into the round
hole of
Merb’s RSpec.

http://github.com/jeremyevans/fixture_dependencies/tree/master

I can answer questions about it if anyone wants to try it.

Except how to make it do rake db:fixtures:load. Does anyone know that
one?


Phlip


#9

Hi all,
Below is my model action.

def activate!
self.update_attribute(:active, true)
end

here is spec/models/user_spec.rb

describe User do
before(:each) do
@user=User.new(:email=>‘removed_email_address@domain.invalid’,:password=‘1234’…,)
end

This is my spec.

it “should activate the user” do
@user.activate!
@user.active.should be_true
end

but it gives error like “#23000Duplicate entry
'removed_email_address@domain.invalid Insert INTO (…) Values(…)”.
It should do update not Insert.
But when i try call it with a new instance of user (with no values
assigned to any user attribute) then it works.
i.e.
it “should activate the user” do
@user_1=User.new
@user_1.activate!
@user_1.active.should be_true
end

I think i have to erase all the User attributes from “before (:each)”
and only assigned it in Examples which required it.

am i right ?

Thanks


#10

kaleem,
method new does not run your model validations, neither does your
provided
activate! implementation.
but save indeed does.
my guess is your mixing several contexts (cases, situations) in your
single
spec. Try decoupling all with more granular examples and you’ll get to
save
shore :slight_smile:
cheers,
joaquin


#11

Hi,

I am trying to write custom matcher which accepts block to spec views
with forms/fieldsets/inputs eg

view:

Personal Information
  1. First name
  2. ...

spec:

it “should have form with input fields” do
render …
response.should have_form(users_path) do
with_field_set ‘Personal Information’ do
with_text_field ‘First name’, ‘user[first_name]’

end
end
end

matches? of have_form:

def matches?(response, &block)
  @block = block if block
  response.should @scope.have_tag('form[action=?]', @action) do
    @block.call.matches?(response)
  end
end matches? of have_field_set: ---

def matches?(response, &block)

  @block = block if block
  response.should @scope.with_tag('fieldset') do
    @scope.with_tag('legend', @legend) if @legend
    @scope.with_tag('ol') do
      @block.call.matches?(response)
    end
  end
end but I get: NoMethodError in '/users/new should have form with

input fields’ undefined method matches?' for true:TrueClass ./spec/views/users/../../custom_ui_matchers/with_field_set.rb:10:inmatches?’
./spec/views/users/…/…/custom_ui_matchers/have_form.rb:11:in
matches?' ./spec/views/users/../../custom_ui_matchers/have_form.rb:10:inmatches?’ ./spec/views/users/new.html.haml_spec.rb:17:
/usr/lib64/ruby/1.8/timeout.rb:53:in `timeout’ something wrong with
yielding block? Gist: http://gist.github.com/62562 Thanks in advance,
Yury


#12

On Thu, Feb 12, 2009 at 5:52 AM, Yury K. removed_email_address@domain.invalid
wrote:

    def matches?(response, &block)
    ./spec/views/users/…/…/custom_ui_matchers/with_field_set.rb:10:in
    matches?' ./spec/views/users/../../custom_ui_matchers/have_form.rb:11:inmatches?’ ./spec/views/users/…/…/custom_ui_matchers/have_form.rb:10:in
    matches?' ./spec/views/users/new.html.haml_spec.rb:17: /usr/lib64/ruby/1.8/timeout.rb:53:intimeout’ something wrong with yielding
    block? Gist: http://gist.github.com/62562 Thanks in advance, Yury

    What does initialize look like?


    #13

    David C. wrote:

    end
    end matches? of have_field_set: —
    end but I get: NoMethodError in '/users/new should have form with input

    initialize for have_form:

    class HaveForm def initialize(action, scope, &block)
    @action, @scope, @block = action, scope, block
    end … end

    def have_form(action, &block)
    HaveForm.new(action, self, &block)
    end

    initialize for have_field_set:

    class WithFieldSet
    def initialize(scope, legend = nil, &block)
    @scope, @legend, @block = scope, legend, block
    end
    end

    def with_field_set(legend = nil, &block)
    WithFieldSet.new(self, legend, &block)
    end

    btw I put it on gist: http://gist.github.com/62562


    #14

    SOLVED! just need to pass matched result to inner block. gist:
    http://gist.github.com/62562

    Many thanks,
    Yury


    #15

    Yury K. wrote:

    SOLVED! just need to pass matched result to inner block. gist:
    http://gist.github.com/62562

    Many thanks,
    Yury

    I was wrong. It yields just one last line from the inner block. It
    passes following:

    it “should have form to create a new user” do
    render ‘/users/new’
    response.should have_form(users_path) do
    with_field_set ‘Personal Information’ do
    with_text_field ‘wrong’, ‘wrong’ # there is no such field
    with_text_field ‘First Name’, ‘user[first_name]’
    end
    end
    end gist: http://gist.github.com/62562 TIA, Yury


    #16

    Yury K. wrote:

    it “should have form with input fields” do
    render …
    response.should have_form(users_path) do
    with_field_set ‘Personal Information’ do
    with_text_field ‘First name’, ‘user[first_name]’

    end
    end
    end

    The minor problem with that system is it forces your test to say exactly
    what
    the code says. That’s not “driven” development!

    If you can forbear to use matchers (shocked gasp!), at my day-job we
    match
    blocks all the time with assert2’s new xpath system:

    require ‘assert2/xpath’

    assert_xhtml response

    xpath :form, :action => users_path do
    xpath :fieldset, ?. => ‘Personal Information’ do
    xpath :input, :type => ‘text’, :name => ‘user[first_name]’ and
    xpath :input, :type => ‘text’, :name => ‘user[last_name]’
    end
    end

    From there, wrapping the xpath() calls up into kewt with_text_field()
    macros
    would be trivial. They could also absolves the redundant ‘user[]’ text
    on the
    names, for example.

    If any inner xpath() fails, there, the fault diagnostic contains a
    formatted &
    indented copy of the HTML block under inspection. The entire page would
    not spew
    out! Only the or would.


    Phlip


    #17

    On Fri, Feb 13, 2009 at 1:53 PM, Phlip removed_email_address@domain.invalid wrote:

    end

    xpath :form, :action => users_path do
    xpath :fieldset, ?. => ‘Personal Information’ do
    xpath :input, :type => ‘text’, :name => ‘user[first_name]’ and
    xpath :input, :type => ‘text’, :name => ‘user[last_name]’
    end
    end

    Hey Philip,

    This looks pretty cool. I wonder if you’d have any interest in making
    this a bit more rspec-friendly? Something like an option to run it
    like this:

    expect_xpath do
    xpath :form, :action => users_path do
    xpath :fieldset, ?. => ‘Personal Information’ do
    xpath :input, :type => ‘text’, :name => ‘user[first_name]’ and
    xpath :input, :type => ‘text’, :name => ‘user[last_name]’
    end
    end
    end


    #18

    David C. wrote:

    This looks pretty cool. I wonder if you’d have any interest in making
    this a bit more rspec-friendly? Something like an option to run it
    like this:

    expect_xpath do

    It’s on my do-list, but…

    …are pluggable matchers as hard to write as the OP implied? How would
    you fix
    his problem? I need to know that before diving into the spaghetti that
    Ruby
    inevitably generates, just below every kewt DSL!


    #19

    Phlip wrote:

    …are pluggable matchers as hard to write as the OP implied? How
    would you fix his problem? I need to know that before diving into the
    spaghetti that Ruby inevitably generates, just below every kewt DSL!

    The major problem is fixed:
    http://github.com/yura/howto-rspec-custom-matchers/

    But there are still few open questions in README.rdoc

    Regards,
    Yury


    #20

    Yury K. wrote:

    The major problem is fixed:
    http://github.com/yura/howto-rspec-custom-matchers/

    Apologies if I missed something, but is that a howto or a library? Do I
    git it
    and run it to … learn to customize matchers?