RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

Thanks for the link! I do hope to cover VCR in a future episode which is more about testing with an external api.

Avatar

Thanks for the introduction to Timecop. It didn't even occur to me that something like that would exist. Thanks!

Avatar

Also have a look at VCR - a great testing tool for recording and playing back http interactions.

Avatar

Does Rails 3.1 now set render_views in the specs/request/.rb tests? In 3.0.9, it seems like only tests in spec/controllers get the right includes automatically? I cannot seem to put render_views in spec/requests/ in 3.0 and get it to work.

Avatar

Thanks you so much for your gist, Jason. Really helpful solution!

Avatar

Thanks for this! - this is really useful - time/web mocking can make tests much much simpler and cleaner.

I'd also like to recommend WebMock
From what I gather, WebMock is a bit more fully-featured (it was even originally inspired by FakeWeb), and also works with non-Net::HTTP - based libraries such as EM-HTTP-Request
Thanks again
Mark

Avatar

By the way the above does a post request (can do gets too which is commented) and it parses the uri including a port which can be something like http://example.com:5000. Parameters is a hash like:
{:file=>'bla.mp4'}

Avatar

I did this to delete that error message

module UsersHelper
def remove_sensitive_error_messages_from_user(user)
@user.errors.full_messages.delete_if { |msg| msg =~ /digest/ }
end
end

and when you want to display error message in views, call that method with a given user object.

Avatar

Wow, allmost like voodoo. Was just about to question how I would test my external web requests properly and bam you come with this episode ;). Awesome!

The thing I might add for asynchronous requests (or requests that can time out) searched a lot around and the only proper way to make a request timeout seems to be as follows:

def http_api_call( url_address, form_parameters )
begin
    uri = URI.parse( url_address )

    #this shortcut works but we can't set the timeouts...
    #response = Net::HTTP.get_response(uri)

    http = Net::HTTP.new(uri.host, uri.port)

    http.open_timeout = 3
    http.read_timeout = 3
    #request = Net::HTTP::Get.new(uri.request_uri)
    request = Net::HTTP::Post.new(uri.request_uri)
    request.set_form_data( form_parameters )

    response = http.request(request)
    result = response.body.to_s
  rescue Timeout::Error
    result = "WARNING: Connection timeout to external url on "+uri.to_s
  rescue Errno::ECONNREFUSED
    result = "WARNING: Connection refused to external url on "+uri.to_s
  end

  result #give back response string
 end

Using the above way your rails process does not hang if the request fails it just times out (had that problem with a backend that would sometimes fail to respond).

Kind regards and keep up the great work!

More test driven development episodes, it's awesome!

Avatar

Hey Ryan,

Thank you for this screencast! I was wondering.. is there a way to "delay" the Password validations when using has_secure_password?

I'm working on a 3-step signup wizard where the last step is the authentication (email and password), however I can't get past the first step because of validation errors (i.e. 2 errors prohibited this user from being saved: * Password digest can't be blank * Password can't be blank).

Any help would be appreciated. Thanks again for your screencasts!!!

J

Avatar

This is fantastic. Really helpful.

You mentioned that the feedback you got was to not include testing in every RAILSCAST episode, which is fair enough. But is there anyway you can maybe include the tests in the show notes as a middle ground - i.e. you do a Railscast on a topic and write the tests as you would have done before doing the coding and have those in show notes even if you don't talk about the tests in the episode?

Avatar

The ids will now contain the custom tokens in quote.
"author_tokens" parameter will now look like 1,2,'Hello',3,'are','you'

So, you may want to modify the following

def author_tokens=(ids)
self.author_ids = ids.split(",")
end

Avatar

Vinicius Depizzol made it possible to create tokens on the fly.

https://github.com/vdepizzol/jquery-tokeninput

Just add allowCustomEntry: true in Application.js

javascript
$(function() {
  $("#book_author_tokens").tokenInput("/authors.json", {
    crossDomain: false,
    prePopulate: $("#book_author_tokens").data("pre"),
    theme: "facebook",
    allowCustomEntry: true
  });
});
Avatar

Do you have a link for that? That's something I would love to see!

Avatar

Not sure if you're still looking at comments - but I see you're not using Cucumber here at all - do you ever use it with rspec? Do you think there's a benefit to using it when you're already using cabybara?

Avatar

Hi Ryan,

I've been trying and trying to start with testing, but it's very easy to get lost. Thanks for this great railscast! I will definately pick up on testing again!
I watch all your eps and love them!

Avatar

I'll never forget watching Pat Maddox and BJ Clark at the Ruby|Web conference in Utah do BDD pair programming live in front of the entire conference. It was amazing. Half of the {B|T}DD tricks I know came from that single demo. The other half will come from this episode. Thanks a ton Ryan!!

Avatar

This is fantastic... would love to see testing in your future episodes. You pack so much already into what you create it doesn't feel right to ask for more :) But would be so appreciated if you add tests when the functionality tested might be something out of the ordinary --- maybe not a good example, but what comes to mind are things like tests to assure background tasks function as expected, as well as maybe just dropping a best practice once in a while.

Anyway keep it up and thanks!

Avatar

I can't get the authorization delete working to save my life. In clicking the 'X' for delete, I get a 'GET' directive to authentications/ for that authentication. No confirmation popup, no delete directive.How I know where it's going: it hits the 'show' method of the controller.

The link_to documentation indicates that this will happen if javascript is not enabled, but javascript IS enabled, as are popups. Am I the only person with this problem?

Avatar

Hi all,

I've had a few people ask, just so you know I ended up having to implement Tableless like this to get it going with Rails 3.1 and working with associations.

Put this in your app/models dir and then extend it to get a tableless model that still supports all the associations like has_many, belongs_to etc, validations and basically smells like a real model.

We use it a fair bit for things like filter models, giving us very clean view and controller code.

https://gist.github.com/1101257

Enjoy.
//matt

Avatar

Just double check check if the omniauth.rb is under initializers dir

Avatar

If you are getting errors like this:

ruby
> agent = WWW::Mechanize.new
NameError: uninitialized constant Object::WWW
        from (irb):7
        from /.rvm/rubies/ruby-1.9.2-p180/bin/irb:16:in `<main>'

I believe WWW::Mechanize.new is now depreciated and should be Mechanize.new instead:

ruby
> agent = Mechanize.new
 => #<Mechanize:0x99960c0 @agent=#<Mechanize::HTTP::Agent:0x999605c

Source: https://webrat.lighthouseapp.com/projects/10503/tickets/368-www-in-wwwmechanize-deprecated

Avatar

In my experience, most of the slowness comes in loading the rails environment for every new test run. You can avoid this with spork. Spork will load your environment once then reload only what it needs for subsequent test runs. To me, it's a must for test driven development with rails.

In addition, you may be able to speed up the tests themselves by eliminating db calls. Ask yourself if you really need a persisted record for your test or if something in memory will do. In most cases, an in memory object with stubs for any associations will be fine for unit tests. Mocking is very powerful and helps you test your code in isolation.

Avatar

Thanks for the excellent screencast (and indeed the whole series).

I have been using Rails for a year now, and have found it a steep learning curve (which I am still climbing). I loved this episode because it does show just how much you need to master in order to do just a tiny part of a system in Rails. Made me feel less stupid.

Try and sit down and watch it from the point of view from someone new to Rails and see how it skips about, demanding knowledge of many gems etc. I know there is a bit of a discussion at the moment about whether Rails is too hard to get into - I think anyone who doubts that should watch this video.

Avatar

I experience that the tests run really slow, is there a way to speed things up?

Avatar

I do have the same problem, using Markdown also.

Would be happy to found a solution :/

Avatar

Man you are a life saver. Thanks for the episode.

Avatar

Where does this section of code should be placed.
Does this code adds auth_token to previously existed users ?? Curiousity!

Avatar

In my opinion, the two main advantages of testing are:

  • Staying focused. By writing tests upfront, and writing only the code needed to make them pass, I don't waste my time on not so important things anymore, but spend it only on things relevant to the task at hand. For this to work, you need to start with high-level tests (just like Ryan does), breaking things down into lower-level tests (model, controller) only when needed.

  • Regression testing. When you start modifying an app you haven't touched for months or even years, what's the best way to ensure you haven't broken anything ? Run the tests !

But of course, there are many others:

  • Documentation. Good, extensive testing can help other developers understand your code and contribute to the project more quickly. Even you can benefit from it.

  • Catching bugs before they even start to fly. Granted, writing tests / specs is slow. This is a feature :) It makes you think about your product twice, and you often find corner-cases you wouldn't have thought of by writing code upfront. Writing one spec, then, can lead you to write several more, which of course will all have to pass by the end of the day...

  • Dopamine. What a warm and fuzzy feeling it is to see all these green dots on the screen ! It helps you staying motivated.

I'm sure other people can think of even more benefits. Of course, there also are pitfalls, like over-testing. But with experience, you'll know when you're doing the right thing, or if something is wrong in your process.

Also, don't forget than testing really starts to pay off on big projects. What may seem useless on artificial examples or on new applications might real soon become necessary. If you start a big project without testing, you will soon regret it !

Hope this helps,

Ga

Avatar

Thanks a million Jean! Hit the bulls eye!! Spot on!!

Avatar

As a beginner to rails and "backend programming" in general, I really cannot see the benefit of these tests... it just seems like extra work to me that can be avoided by just using the app and entering dummy data to see if everything works.

Could someone perhaps explain to me why it is important to do tests this way instead of via the browser? Like an article on why testing is important?

Avatar

Ryan,

Great screencast. Like most everyone else, I love the idea of having some Railscasts include a focus on testing. I'd really like to see more real-world TDD/BDD style projects, even if they span multiple screencasts.

Avatar

There's a public folder in ..../gems/resque-1.17.1/lib/resque/server/public -- if you copy those files to RAILS_ROOT/public/resque it'll show in production.

Avatar

I have the same problem. Only difference I can see so far is that I'm using it with rails 3.0.9 instead of 3.1.

Awesome railscast btw as usual. =)

Avatar

Thank you for this, Ryan.
Really, really useful.

Avatar

Yeah Wow

That was so jam packed. Amazing.
I think i need to watch it in slow mo.

Avatar

It certainly does slow down your development speed up front, but once you get into the flow of it, ensuring that your entire code base is covered with tests is absolutely invaluable. Especially when you are working on a large project or just coming back to an old one. The amount of time that is saved from debugging is more than the time that is spent writing tests.

Of course, it is all about finding the balance that works for you. It's just a matter of trying it out for long enough to actually experience the benefits of it.

Avatar

the issue is that the episodes would be too long, and it would muddy whatever else I'm trying to teach. This is why I opted to keep the testing episodes separate.

I totally understand the issue of length for each episode. Having an episode focused on building a feature then having another focused on building it with TDD seems like a great approach. It avoids the issue of length and gives you the time to focus on both aspects (functionality and testing) adequately.

Avatar

Thanks Ryan.

But how to mix this RailsCast with the #196 (http://railscasts.com/episodes/196-nested-model-form-part-1) ?

I've tried "accepts_nested_attributes_for :children", any ideas to do this?

Thanks a lot ^^

Avatar

I was wondering how do you use very handy webrat view and controller marchers as those are conflicting with capybara?
I decided that webrat marchers are more important for my workflow and had to get rid of capybara.

What is your take on that?

Cheers,
Dmytrii.

Avatar

Anyone knows why indentation is not correct, or which part is responsible for that, Albino or pygmentize?

it looks like this

ruby
def syntax_highlighter(html)
                doc = Nokogiri::HTML(html)
                  doc.search("//pre[@lang]").each do |pre|
                    pre.replace Albino.colorize(pre.text.rstrip, pre[:lang])
                  end
                doc.css('body').inner_html.to_s
            end

instead of

ruby
def syntax_highlighter(html)
  doc = Nokogiri::HTML::DocumentFragment.parse(html)
  doc.css("pre[@lang]").each do |pre|
    pre.replace Albino.colorize(pre.text.rstrip, pre[:lang])
  end
  doc.to_s
end 

when I put this without parsing it with Nokogiri/Albino/Pyg, then it appears correct (using markdown only)

Avatar

Bye bye Authlogic/Devise, hello Ryan's "authentication from scratch" + CanCan. Thank you.

@Iain

"Devise is childishly easy to customize".

That's only true for simple, small applications.

"There are too many security pitfalls to remember"

And that's true for Devise customizations in more complex scenarios ;)

Avatar

Ryan. I just wanted to add to this sentiment.

While there was much more to wrap my head around than one of your typical screen-casts, the process you are using here is fundamentally different than the typical fleshing out of features without tests. Seeing you test, step by step is invaluable, and I would love to see more of it.

Avatar

Actually, you don't need to have a:

ruby
validates_presence_of :password, :on => :create

line in the User model, as under the hood there is a:

ruby
 validates_presence_of password_digest

in ActiveModel lib under active_model/secure_password.rb.
Unfortunately, it has no option to customize the message, making it a nightmare for internationalization.

Avatar

This seems like it would really slow down my development speed.

Does your company require you to write tests first?

Avatar

Super awesome! Ryan, have you seen any good documentation on all the methods available in capybara to do request specs?

I almost jumped into it at one point, but could never find the equivalent document for it like this one: http://guides.rubyonrails.org/testing.html

This video is a great overview, but was wondering if you knew of a capybara reference to kept open to see all available methods?

Avatar

I fixed this by changing the sytnax_highlighter method to

ruby
  def syntax_highlight(html)
    doc = Nokogiri::HTML(html)
      doc.search("//pre[@lang]").each do |pre|
        pre.replace Albino.colorize(pre.text.rstrip, pre[:lang])
      end
    doc.css('body').inner_html.to_s
  end
Avatar

Very useful episode, your level of abstraction seems optimal and focused on features.
Unfortunatly often a not so obvious knowledge is needed to perform some kind of tests.