Forum: RSpec Test::Unit Functional failure puzzle

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2008-11-20 20:30
Not an RSpec question, but I was led to this problem by starting to use
autotest with cucumber and so, however unjustly, I feel that RSpec has
to shoulder some of the blame.

I have a failing test in my clients_controller test and I cannot figure
out what is wrong from the information provided from the test results
and the logs.  I would appreciate it someone here to take a look at this
and show me what I am missing.

The test:

class ClientsControllerTest < ActionController::TestCase

  fixtures  :entities, :clients

  def test_should_create_client
    assert_difference('Client.count') do
      post :create, :client =>  { :entity_id => 1,
           :client_status => 'HOLD',
           :client_credit_policy => 'CASH',
           :client_credit_terms => 0,
           :effective_from => "19841101000000".to_date,
           :superseded_after => "20141031235959".to_date
      }
    end

    assert_redirected_to client_path(assigns(:client))
  end


The model:

class Client < ActiveRecord::Base

  belongs_to                  :entity

  validates_associated
  validates_presence_of       :effective_from
  validate                    :date_range

  private

  def date_range
    unless effective_from
      errors.add_to_base("An Effective date is required") \
    end
    if  superseded_after
      errors.add_to_base("Superseded date falls before Effective date")
\
        if superseded_after < effective_from
    end
  end

end

The test result:

/usr/bin/ruby -Ilib:test
"/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb"
"test/functional/clients_controller_test.rb"
"test/functional/entity_client_controller_test.rb"
Loaded suite
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
Started
F........
Finished in 1.096921 seconds.

  1) Failure:
test_should_create_client(ClientsControllerTest)
    [/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/core_ext/test/unit/assertions.rb:51:in
`assert_difference'
     /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.1/lib/action_view/renderable.rb:81:in
`each_with_index'
     /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/core_ext/test/unit/assertions.rb:47:in
`each'
     /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/core_ext/test/unit/assertions.rb:47:in
`each_with_index'
     /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/core_ext/test/unit/assertions.rb:47:in
`assert_difference'
     ./test/functional/clients_controller_test.rb:19:in
`test_should_create_client'
     /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/setup_and_teardown.rb:60:in
`__send__'
     /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.1/lib/active_support/testing/setup_and_teardown.rb:60:in
`run']:
<Client.count> was the expression that failed.
<3> expected but was
<2>.

9 tests, 10 assertions, 1 failures, 0 errors
/usr/bin/ruby -Ilib:test
"/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb"
Errors running test:functionals!


So, the test client is not being inserted.  But I do not see in the
test.log any sql that does an INSERT so I am at a loss to explain what
is happening.

The log:

Processing ClientsController#new (for 0.0.0.0 at 2008-11-20 14:21:19)
[GET]
Rendering template within layouts/application
Rendering clients/new
Rendered entities/_entity_header (4.6ms)
Rendered clients/_client_detail (3.5ms)
Rendered shared/_effective_period (3.5ms)
Completed in 28ms (View: 24, DB: 4) | 200 OK
[http://test.host/clients/new]
  Client Load (1.5ms)   SELECT * FROM "clients" WHERE ("clients"."id" =
953125641)


Processing ClientsController#show (for 0.0.0.0 at 2008-11-20 14:21:19)
[GET]
  Parameters: {"id"=>"953125641"}
  Client Load (1.6ms)   SELECT * FROM "clients" WHERE ("clients"."id" =
953125641)
Rendering template within layouts/application
Rendering clients/show
  Entity Load (1.2ms)   SELECT * FROM "entities" WHERE ("entities"."id"
= 1)
Completed in 17ms (View: 10, DB: 8) | 200 OK
[http://test.host/clients/953125641]
  Client Load (1.5ms)   SELECT * FROM "clients" WHERE ("clients"."id" =
953125641)


Processing ClientsController#update (for 0.0.0.0 at 2008-11-20 14:21:20)
[PUT]
  Parameters: {"client"=>{}, "id"=>"953125641"}
  Client Load (1.6ms)   SELECT * FROM "clients" WHERE ("clients"."id" =
953125641)
Redirected to #<Client:0xb71808c8>
Completed in 12ms (DB: 11) | 302 Found
[http://test.host/clients/953125641?]
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2008-11-21 01:10
(Received via mailing list)
On Thu, Nov 20, 2008 at 1:30 PM, James Byrne <lists@ruby-forum.com>
wrote:
>
>           :effective_from => "19841101000000".to_date,
> class Client < ActiveRecord::Base
>    unless effective_from
>
> Finished in 1.096921 seconds.
> `each_with_index'
> <2>.
>
> 9 tests, 10 assertions, 1 failures, 0 errors
> /usr/bin/ruby -Ilib:test
> "/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb"
> Errors running test:functionals!
>
>
> So, the test client is not being inserted.  But I do not see in the
> test.log any sql that does an INSERT so I am at a loss to explain what
> is happening.

Without seeing the controller code, I'd guess that the controller uses
create, and not create! and that a validation failure is not getting
reported anywhere.

If so, try changing create to create! and you should get your error.

If not, please post the controller code so we can see what else might
be up there.

Cheers,
David
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2008-11-21 15:44
David Chelimsky wrote:

>
> Without seeing the controller code, I'd guess that the controller uses
> create, and not create! and that a validation failure is not getting
> reported anywhere.
>
> If so, try changing create to create! and you should get your error.
>

Thank you. You were close enough to the actual situation that I could
easily see what had to be done.

The main problem (other than my abysmal ignorance) is that I am
retro-fitting test to code that I wrote some time ago.  In this case
client is a role associated to an entity.  The clients_controller
handles the case where the entity and the client are created together in
one pass.  I was creating a new client but not a new entity in my test
case and so the save was failing because the entity validations were
failing.  Changing the .save to .save! displayed the actual error in the
test and made the entire situation clear to me.

My next question is: why would one choose .save/.create over
.save!/.create! since the former does not render the error when testing?
F86901feca747abbb5c6c020362ef2e7?d=identicon&s=25 Zach Dennis (zdennis)
on 2008-11-24 16:46
(Received via mailing list)
On Fri, Nov 21, 2008 at 9:44 AM, James Byrne <lists@ruby-forum.com>
wrote:
> Thank you. You were close enough to the actual situation that I could
>
> My next question is: why would one choose .save/.create over
> .save!/.create! since the former does not render the error when testing?

It's very common that whoever was filling out a form will want to be
notified of any validation
errors that may exist from their data entry. If you use #save you can
simply check to
see if it returned true or false, and then render the appropriate
template. e.g:

  def create
      @user = User.new params[:user]
      if @user.save
         # yay success
      else
         render :action => "new"
      end
   end

If you use #save! then your controller action has to rescue the
exception in order to render the right page. Based on where/how you
assign the instance variables you may need to introduce a inner
begin/rescue block which makes the action more clunky and less
aesthetically appealing.

  def create
      @user = User.new params[:user]
      @user.save!
  rescue ActiveRecord::RecordInvalid
      render :action => "new"
  end

If you use .create! then your controller action doesn't have access to
an instance variable which may be needed to render the appropriate
template.

  def create
      @user = User.create! params[:user]
  rescue ActiveRecord::RecordInvalid

 render :action => "new"
  end



--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
F86901feca747abbb5c6c020362ef2e7?d=identicon&s=25 Zach Dennis (zdennis)
on 2008-11-24 16:59
(Received via mailing list)
Bah, hit send on accident... here's the last create! example...

  def create
      @user = User.create! params[:user]
  rescue ActiveRecord::RecordInvalid
     # this won't work, because @user never gets assigned, since
create! raised an exception
     render :action => "new"
  end

In any of the cases where you have to rescue an exception in a
controller action that will have the same affect of your test not
reporting the exception. If your application can handle returning a
default error page to the user then you're probably okay, but if you
need to re-render something for the user (like the data entry form)
then you won't want un-rescued exceptions for things like validation
to occur. And then you may not want to rescue exceptions at all, and
you might just end back up with #save.

HTH,

--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
48641c4be1fbe167929fb16c9fd94990?d=identicon&s=25 Mark Wilden (Guest)
on 2008-11-24 17:45
(Received via mailing list)
On Sun, Nov 23, 2008 at 10:51 AM, Zach Dennis <zach.dennis@gmail.com>
wrote:

> controller action that will have the same affect of your test not
> reporting the exception. If your application can handle returning a
> default error page to the user then you're probably okay, but if you
> need to re-render something for the user (like the data entry form)
> then you won't want un-rescued exceptions for things like validation
> to occur. And then you may not want to rescue exceptions at all, and
> you might just end back up with #save.
>

This is just an example of the general principle that one should not use
exception handling for control flow. Exceptions were designed in Ruby
(as in
other languages) to handle exception circumstances; in particular, when
the
code that causes the problem does not necessarily know how to handle the
problem. Then, exception handling represents a sort of non-local goto.
For
unexceptional circumstances (such as a user leaving a form field blank),
exceptions are expensive and harder to understand than simply checking
the
return value of a method.

That said, I see lots of code where #save and #create are called without
checking the return value. I don't bother checking the return value when
"nothing could go wrong." But that's what #save! and #create! are for.

///ark
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2008-11-24 17:50
(Received via mailing list)
On 21 Nov 2008, at 14:44, James Byrne wrote:

>
> failing.  Changing the .save to .save! displayed the actual error in
> the
> test and made the entire situation clear to me.
>
> My next question is: why would one choose .save/.create over
> .save!/.create! since the former does not render the error when
> testing?

The obvious answer is that you may well have a use case where it's
perfectly OK to attempt to save or at least create an invalid object.

An example would be a user sign-up page where the User object created
from the values put into the form was invalid, but you wanted to just
feed back the validation failures to the user so they could have
another crack at the form. I can imagine some people might do this
with a call to #create! wrapped up in a begin/rescue block, but I've
always found that sort of thing rather clumsy myself. I'd prefer to
see the code check the User#valid? state after a call to #create, then
act accordingly.

Make sense?
Matt
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2008-11-24 18:56
Matt Wynne wrote:

>
> Make sense?
> Matt

Yes.  I am afraid that my original post is based upon a naive sense of
how things work in this environment.  Clearly, whenever one is dealing
with human input then the possibility of incomplete, contradictory, or
simply wrong data must be accommodated as elegantly as is possible.  I
was overmuch concerned with testing at that moment to give the other
issues their due.

I have since run across the construct shown below and have used it in
the functional test rather than changing the controller code for
testing.

# This code extends the subject controller to
# display errors raised in the controller code
# in the test results
require 'clients_controller'
class ClientsController; def rescue_action(e) raise e end; end
# back to our test
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2008-11-24 19:38
(Received via mailing list)
On 24 Nov 2008, at 17:56, James Byrne wrote:

> was overmuch concerned with testing at that moment to give the other
> class ClientsController; def rescue_action(e) raise e end; end
> # back to our test

Careful. That looks to me like you're changing the controller code for
testing.

How come your controller doesn't raise these errors anyway?


I think you have a deeper problem here. The reason you asked this
question, IIRC, is because your test code created a model which was
not valid, and therefore when the controller tried to save it, you got
some behaviour you didn't expect. You might want to look at using a
more standardised mechanism for creating test objects, such as the
factory_girl plug-in from throughbot. That way, you can test that each
of the objects produced by the factory are valid, and you won't get
tripped up like this in the future.

At the very least, when you create your test object instance, just
quickly check that it's valid:

  before(:each) do
    @dog = Dog.new(:legs = 3)
    @dog.should be_valid
  end

cheers,
Matt
This topic is locked and can not be replied to.