I can't believe no one has taken to calling you Dr. Bates yet. Not just for your coding examples, but also because of your ESP in delivering these episodes exactly when others are in need of them. Thanks again!
Ryan,
As usual, relevant info presented succinctly with style.
How does Guard compare with Autotest (and Spork)?
I've been using autotest/spork for a while now, and it works well, but if you change a controller or routes the proper specs are run, but fail because spork is running the old version. Does Guard do this better?
Would be really helpful if searchlogic would be ported to rails 3. I want to upgrade a rails 2.3 project to rails 3, would hate to have to remove searchlogic.
Oh well.. that's a problem with most rails plugins/gems - they stop being upgraded after a while and when you have an older project that uses them then you are out of luck.
In case anyone else is having problems getting this to work with gmail apps, there is a little error in Ryan's demo code. In mail_setup.rb, user_name must be fully qualified, as in user@domain.tld, not just user. In the example, Ryan has
:user_name => "railscasts"
...where it should be...
:user_name => "railscasts@railscasts.com".
thanks for the great episode. After watching it it was keen trying it myself and downloaded the complete source code. After extraction I installed the bundle, did a "rake db:migrate" and tested the app in the browser, all worked fine.
When following the coding using the auth-before tree as a start I soon got this error:
Failure/Error: let(:user) { Factory(:user) }
ActiveRecord::StatementInvalid:
Could not find table 'users'
I checked in the auth-after (bundle, rake db:migrate, guard) and I do get the same error. So there must be something wrong with my testing environment as the regular development environment find the user table in the browser. I am on Ubuntu Linux, I installed/updated libnotify, rb-inotify, and childprocess as guard did complain about not finding them or them being out-dated...
You're right. The Timecop version is more deterministic and that is what our tests should be.
But there maybe situations where you can't use Timecop and you don't need to test on milliseconds (with an increased delta to get a more robust test), then it might be a good solution too.
I have a few small simplifications that I'll share. I'm new to Rails (but not programming in general) so I apologize if anything is bad practice.
ruby
# In the User model. This is now an instance method.defauthenticate(password)
selfifPassword.new(password_hash) == password
enddefencrypt_passwordself.password_hash = Password.create(password)
end
ruby
# In the users controller. defcreate
user = User.find_by_email(params[:email]).authenticate(params[:password])
...etc...
end
It can have unpredictable behaviour. What if send_password_reset first sets attribute value and then does something longer than a second due to high system load? What if send_password_reset doesn't really set attribute value but some previous test does and tests are not fully isolated? You'll have test that breaks from time to time in random manner.
If somebody gets stuck on the part when form doesn't generate nested fields <% f.fields_for :questions do |builder| %>
then in Rails 3.1 you have to change the tags to <%= %>
For better security you want to save also a expiry time inside the cookie and sign it. Then you can check e.g. that the cookie isn't more than year old so that info in old computers cannot be abused. But in most cases the way described in this screencast is enough.
hey guys please what tmbundle or snippet allows you to type in 2 place at the same time...for instance when you open a p tag, if you change the opening tag to strong, the closing ta automatically changes too. He used something like that around 2:45 - 2:50
Testing with times involved is mostly no fun, particularly because I never heard of Timecop before :) But I found a way to manage it with RSpec matchers:
ruby
it "saves the time the password reset was sent"do
user.send_password_reset
user.reload.password_reset_sent_at.should be_within(1.second).of(Time.zone.now)
end
I'm having a hard time understanding how the successful "signed up!" message appears as shown in the video, esp when comparing it to the downloadable episode code. There doesn't seem to be anything in a View or Layout to output flash[:notice]
I have a small question about this testing approach. Since I'm not testing a "controller", I don't have the session/params/cookies variables to work with.
My authentication depends on storing session[:user_id] but I can't set it from the integration test since I get that session is not defined.
Is it possible to make the method of geocoding conditional? i want to geocode by ip_address by default but if the user updates there current address, I want to then geocode by that.Anyone have ideas on how to do that? something like
Does anyone have a problem with Internet Explorer? I couldn't get the session store accross subdomains working well...
It works fine most of the times, but sometimes it seems the session is different between subdomains... example: if I log out from subdomain1.domain.com and after that I visit subdomain2.domain.com for some reason I am still log in!
The behavior is strange, but it just happen in Internet Explorer =/ and, as I said before, it only occure sometimes...
I've been trying to get the user_id of whoever created the new items to be put into the column on the authors table. Been trying like this Author.create!(:name => $1, :user_id => self.user_id) but I keep getting NULL entries into the database. Any ideas?
Any idea how to get the user_id from the user who posted the entry? been trying Author.create!(:name => $1, :user_id => self.user_id) but I keep getting NULL entries into the db.
Since Rails 3.1 comes with CoffeeScript installed we get the gem ExecJsExecj that "lets you run JavaScript code from Ruby" but you need to have a Javascript Runtime installed. ExecJs chooses the best runtime that we have.
You can install any of these runtimes:
therubyracer - Google V8 embedded within Ruby
therubyrhino - Mozilla Rhino embedded within JRuby
Johnson - Mozilla SpiderMonkey embedded within Ruby
Mustang - Mustang V8 embedded within Ruby
Node.js
Apple JavaScriptCore - Included with Mac OS X
Mozilla SpiderMonkey
Microsoft Windows Script Host (JScript)
I recommend to install TheRubyRacer or Johnson because are easy to install, You can install them with gems.
Ok interesting to know. I was trying to stick with that as well, but I really ran into trouble when I wanted to test redirects and urls. Maybe there's a way to do this (cleanly), but I couldn't figure it out. Say you wanted to test a very simple create action that redirects to the show page. In the integration test you have something like:
at this point, how can you access the controller variables to ensure that you redirect to user_path(@user). assigns(:user) wasn't working, and after days of searching, came to the conclusion this sort of test is best served in the controller specs. I supposed you could do something like:
I don't do controller or view tests because I feel they overlap too much with integration tests. Usually if something is too complex to go in an integration test then it should be moved into a model or helper and tested there in isolation. This makes it easier to handle a lot of branching paths and such as well.
I'm not very strict on refactoring test code, I feel it's better to be more direct there to reduce the possibility of bugs and odd dependencies. But when I do see a lot of duplication I like to move it into a module in the spec/support directory and do config.include in the spec_helper.rb
If your users use internet explorer, the buttons and functionality doesn't even work! Is there a way to progressively enhance your usability by using pushState?
I think it's still good to have a Timecop.return statement in spec_helper.rb in case something slips through, but I do think the block syntax is great for larger test cases where you want to isolate where it is frozen.
Excellent cast as always, and I really like the testing angle. I've got a question though and I was hoping you or anyone else could help me out. I fairly experienced in the app folder, but not quite as much in spec. But alas, I have taken on Ryan's challenge last week and haven't opened the browser since. It's been going great, and I have tons of test coverage, but I'm worried I may have too much - or at least redundant tests. Specifically, I see a lot of overlay between my integration tests and my controller tests. I find that whichever one I write first, it covers about 75% of the other one. That really bugs me. So a few things:
Is this normal? I get the feeling it isn't because there really wouldn't be 2 seperate areas to test the same thing.
What's some good practices to make sure that there's little overlap between the 2 sections.
Do you always start with integration tests, then more to controller, or what are the common coding flows that people use.
I'm very good at refactoring app code, but test code, not so much, any suggestions on how to really learn good test code refactoring?
I can't get the password_validation to work properly. Whatever I enter in password_confirmation (even if it's not the same value as password), it'll save the user regardless :(
I can't remember now why I chose FakeWeb over WebMock when I used it in railscasts.com, but it definitely does look more full featured. Thanks for bringing this up. Time to research it.
Hey Ryan, quick question, how do you get your project folder to be part of the textmate window rather than the usual drawer style (like this; http://blogallalong.com/wp-content/uploads/2009/04/textmate-project-drawer.png) ?
Thanks for the all superb videos over the years! :)
@hounddog @jowls
The
mass_assignment_authorizer
method now takes arole
parameter in Rails 3.1.You can see for yourself here.
I forgot to add that with the above changes, you can remove the "password_salt" column.
I can't believe no one has taken to calling you Dr. Bates yet. Not just for your coding examples, but also because of your ESP in delivering these episodes exactly when others are in need of them. Thanks again!
Hi,
when I try
I get
but not get
<pre lang="ruby">
Thanks for the podcast.
Ryan,
As usual, relevant info presented succinctly with style.
How does Guard compare with Autotest (and Spork)?
I've been using autotest/spork for a while now, and it works well, but if you change a controller or routes the proper specs are run, but fail because spork is running the old version. Does Guard do this better?
Would be really helpful if searchlogic would be ported to rails 3. I want to upgrade a rails 2.3 project to rails 3, would hate to have to remove searchlogic.
Oh well.. that's a problem with most rails plugins/gems - they stop being upgraded after a while and when you have an older project that uses them then you are out of luck.
In case anyone else is having problems getting this to work with gmail apps, there is a little error in Ryan's demo code. In mail_setup.rb, user_name must be fully qualified, as in user@domain.tld, not just user. In the example, Ryan has
:user_name => "railscasts"
...where it should be...
:user_name => "railscasts@railscasts.com".
Even I have the same doubt
Hi Ryan,
thanks for the great episode. After watching it it was keen trying it myself and downloaded the complete source code. After extraction I installed the bundle, did a "rake db:migrate" and tested the app in the browser, all worked fine.
When following the coding using the auth-before tree as a start I soon got this error:
I checked in the auth-after (bundle, rake db:migrate, guard) and I do get the same error. So there must be something wrong with my testing environment as the regular development environment find the user table in the browser. I am on Ubuntu Linux, I installed/updated libnotify, rb-inotify, and childprocess as guard did complain about not finding them or them being out-dated...
Any help would be appreciated.
Cheers Juergen
SOLVED:
rake db:test:prepare
Should have guessed... sigh
You're right. The Timecop version is more deterministic and that is what our tests should be.
But there maybe situations where you can't use Timecop and you don't need to test on milliseconds (with an increased delta to get a more robust test), then it might be a good solution too.
I cannot get capybara to work on rails 3.1.0.rc5.
I wonder if somebody knows how to make it work.
This railscast extends work done in the Authentication from Scratch railscast. You'll find the missing code in there.
I have a few small simplifications that I'll share. I'm new to Rails (but not programming in general) so I apologize if anything is bad practice.
It can have unpredictable behaviour. What if send_password_reset first sets attribute value and then does something longer than a second due to high system load? What if send_password_reset doesn't really set attribute value but some previous test does and tests are not fully isolated? You'll have test that breaks from time to time in random manner.
Maxsy and airyym,
Regarding the "uninitialized constant User::BCrypt" problem, you need to run "bundle install" in the terminal and then restart the rails server.
If somebody gets stuck on the part when form doesn't generate nested fields
<% f.fields_for :questions do |builder| %>
then in Rails 3.1 you have to change the tags to
<%= %>
I've wasted hours on that...
That's what I've been doing as well. I'm giving Timecop a try now and it's pretty good, not much changed except a few tests so far.
It should be
cookies.delete(:auth_token)
, notcookies.delete[:auth_token]
.Signing cookie adds overhead to the size (http://yuiblog.com/blog/2007/03/01/performance-research-part-3/). So that depends on the security you need.
For better security you want to save also a expiry time inside the cookie and sign it. Then you can check e.g. that the cookie isn't more than year old so that info in old computers cannot be abused. But in most cases the way described in this screencast is enough.
I can confirm that this method works. Thanks for the example.
hey guys please what tmbundle or snippet allows you to type in 2 place at the same time...for instance when you open a p tag, if you change the opening tag to strong, the closing ta automatically changes too. He used something like that around 2:45 - 2:50
Testing with times involved is mostly no fun, particularly because I never heard of Timecop before :) But I found a way to manage it with RSpec matchers:
(newbie alert)
I'm having a hard time understanding how the successful "signed up!" message appears as shown in the video, esp when comparing it to the downloadable episode code. There doesn't seem to be anything in a View or Layout to output flash[:notice]
Your just too awesome !! I am blind with true awesomeness :)
I thought exactly the same. I always just mock Time.zone.now (or Time.now) to test if the time gets set.
I really would like to hear what the benefits of using timecop are if you only want to test if the time gets set to Time.zone.now
Hi Ryan,
I have a small question about this testing approach. Since I'm not testing a "controller", I don't have the session/params/cookies variables to work with.
My authentication depends on storing session[:user_id] but I can't set it from the integration test since I get that session is not defined.
How can I get around that?
Thanks!
whoops, had those geocoded_by calls reversed
Is it possible to make the method of geocoding conditional? i want to geocode by ip_address by default but if the user updates there current address, I want to then geocode by that.Anyone have ideas on how to do that? something like
Hey, if anyone if having the same problem, you need to use preserve before sending the data.
[source]
http://stackoverflow.com/questions/6325416/maruku-incorrectly-parsing-second-line-of-code-blocks
Hi Ryan, fantastic episode as alwais.
Do You know a way to test file uploads? e.g size, timeout, type of files (mime), image dimensions,.. ?
And where to put this stub files?
My first impulse when watching the first part of the episode was "just mock the Time.zone method so that Time.zone.now returns a fixed date"
Wouldn't that have been enough?
Does anyone have a problem with Internet Explorer? I couldn't get the session store accross subdomains working well...
It works fine most of the times, but sometimes it seems the session is different between subdomains... example: if I log out from subdomain1.domain.com and after that I visit subdomain2.domain.com for some reason I am still log in!
The behavior is strange, but it just happen in Internet Explorer =/ and, as I said before, it only occure sometimes...
Any ideas?
I've been trying to get the user_id of whoever created the new items to be put into the column on the authors table. Been trying like this Author.create!(:name => $1, :user_id => self.user_id) but I keep getting NULL entries into the database. Any ideas?
Using this gist you can also add custom entries.
https://gist.github.com/952240
Any idea how to get the user_id from the user who posted the entry? been trying Author.create!(:name => $1, :user_id => self.user_id) but I keep getting NULL entries into the db.
For some reason
cookies.delete[:auth_token]
does not work in Rails 3.1.I had to use
cookies.delete :auth_token
to get it to destroy the session.Ryan Bates is a God!!
I just installed the Rails 3.1.0.rc5 version and I found that the line:
config.active_record.identify_map = true
on application.rb has been removed, this must be cause that intruction was not working at all
Since Rails 3.1 comes with CoffeeScript installed we get the gem ExecJs Execj that "lets you run JavaScript code from Ruby" but you need to have a Javascript Runtime installed. ExecJs chooses the best runtime that we have.
You can install any of these runtimes:
I recommend to install TheRubyRacer or Johnson because are easy to install, You can install them with gems.
Ok interesting to know. I was trying to stick with that as well, but I really ran into trouble when I wanted to test redirects and urls. Maybe there's a way to do this (cleanly), but I couldn't figure it out. Say you wanted to test a very simple create action that redirects to the show page. In the integration test you have something like:
at this point, how can you access the controller variables to ensure that you redirect to user_path(@user). assigns(:user) wasn't working, and after days of searching, came to the conclusion this sort of test is best served in the controller specs. I supposed you could do something like:
current_page.should eq(user_path(User.last))
but that didn't seem right at all.
I don't do controller or view tests because I feel they overlap too much with integration tests. Usually if something is too complex to go in an integration test then it should be moved into a model or helper and tested there in isolation. This makes it easier to handle a lot of branching paths and such as well.
I'm not very strict on refactoring test code, I feel it's better to be more direct there to reduce the possibility of bugs and odd dependencies. But when I do see a lot of duplication I like to move it into a module in the spec/support directory and do
config.include
in the spec_helper.rbIf your users use internet explorer, the buttons and functionality doesn't even work! Is there a way to progressively enhance your usability by using pushState?
I think it's still good to have a
Timecop.return
statement inspec_helper.rb
in case something slips through, but I do think the block syntax is great for larger test cases where you want to isolate where it is frozen.Excellent cast as always, and I really like the testing angle. I've got a question though and I was hoping you or anyone else could help me out. I fairly experienced in the app folder, but not quite as much in spec. But alas, I have taken on Ryan's challenge last week and haven't opened the browser since. It's been going great, and I have tons of test coverage, but I'm worried I may have too much - or at least redundant tests. Specifically, I see a lot of overlay between my integration tests and my controller tests. I find that whichever one I write first, it covers about 75% of the other one. That really bugs me. So a few things:
Thanks so much everyone.
Ken
I can't get the password_validation to work properly. Whatever I enter in password_confirmation (even if it's not the same value as password), it'll save the user regardless :(
Great screencast as always, thanks!
One possible improvement (at least for me) is the block usage of Timecop#freeze.
Instead of having to call #return explicitly, I prefer to put the frozen code in a block, like:
That way it automatically returns from the frozen time and also makes explicit which code is frozen via indentation.
Hi Ryan.
Have you thought about doing an episode about testing ajax functionalities?
Cheers, Kai
I'm using nested forms to which I add fields dynamically with ryans gem nested_form. How could these fields be validated?
Figured it out.. If anyone needs help with the same issue, refer here: http://stackoverflow.com/questions/6819481/using-jquery-tokeninput-without-default-name/6819909#6819909
How can change the default from "id" and "name" to something other than name (in my case its "account_number").
In the video he just says if you dont have "name", youll need further customization and i couldnt find anything anywhere.
Any help?
I can't remember now why I chose FakeWeb over WebMock when I used it in railscasts.com, but it definitely does look more full featured. Thanks for bringing this up. Time to research it.