[rspec-users] Testing url_for

David Chelimsky dchelimsky at gmail.com
Tue Oct 27 20:21:14 EDT 2009

On Oct 27, 2009, at 6:48 PM, Rodrigo Rosenfeld Rosas <lbocseg at yahoo.com.br 
 > wrote:

> Em 27-10-2009 15:17, David Chelimsky escreveu:
>> On Oct 27, 2009, at 8:21 AM, Rodrigo Rosenfeld Rosas wrote:
>>> Hi David, I'm giving a try to RSpec after we meet each other on  
>>> Rails Summit Latin America and I must admit I'm enjoying using  
>>> rspec/machinist/faker.
>>> Since I have not written any controllers yet, I hadn't taken a  
>>> chance to try webrat.
>>> But there is a situation that I would like some feedback on how to  
>>> deal with it.
>>> When registering new users, they will input their e-mail and a  
>>> message will be sent for them to confirm their addresses and  
>>> continue registering.
>>> I use something like:
>>> MailNotifier.deliver_email_confirmation_message :confirmation_url  
>>> => url_for(:controllers => 'users', :action =>  
>>> 'continue_register', :user => @user.id, :token =>  
>>> @user.confirmation_token)
>>> And the routes are set to ':controller/:action', so that the url  
>>> would translate to '/users/continue_register? 
>>> user=2&confirmation_token=asdf987asf'.
>>> The problem is that Ruby 1.8 will not maintain any specific order  
>>> for the parameters. (The application is hosted in a shared server  
>>> at hostingrails.com, which hosts Ruby 1.8)
>>> I know that I could add a route to generate '/users/ 
>>> continue_register/2/asdf987asf' instead, but I would still like to  
>>> know what would be the alternatives.
>> Oi Rodrigo,
>> There's no great alternative that I know of. I've always just  
>> grabbed the URL using a regexp and then broken it up. Something  
>> like this:
>> text.should =~ /http:\/\/test\.host\/users\/continue_register\?([^ 
>> \s]*)/
>> query_string = $1
>> query_string.should =~ /user=2/
>> query_string.should =~ /confirmation_token=asdf987asf/
>> You could also use Rack::Utils.parse_query to convert the  
>> query_string to hash, or capture the entire url and use the  
>> route_to matcher:
>> text.should =~ /(http:\/\/test\.host\/users\/continue_register\?[^ 
>> \s]*)/
>> {:get => $1}.should route_to(
>>  :controllers => 'users',
>>  :action => 'continue_register',
>>  :user => @user.id,
>>  :token => @user.confirmation_token
>> )
>> I don't love either of those - I'd sooner change the implementation  
>> to something deterministic, but that's me :)
> David, thank you very much for your feedback, although not ideal  
> they are good approaches.
> I understand your point of view in changing to something  
> deterministic and I was thinking in doing that, at first. But I  
> would like to know if there was a gotcha that I couldn't figure it  
> out. I need to choose between clean code in the implementation or in  
> the test...
> Using a deterministic approach would make the test clearer, while  
> using url_for with Ruby 1.8 would make the implementation clearer...  
> That is why I was thinking in creating a route to achieve both, but  
> it would exist only to make both clearer and could cause some  
> confusion while reading routes.rb...
> The first approach you presented is clearer, but wouldn't detect a  
> problem that I noticed when testing my mailer. I was using  
> RedCloth's textilize, thinking that it would generate a link (which  
> actually auto_link does) and I was testing if the link was contained  
> in the body. The first approach wouldn't detect that the url was  
> wrong, like "?user=1&amp;token=abc".
> The second approach goes against Ruby way, in my opinion... :) Too  
> many code to test a single statement...
> Another approach would be something like:
> Regexp.new('http://test.host/users/continue_register\?'+(['([^\s]+)'] 
> *2).join('\&')).
>   match(email.body).to_a[1..-1].to_set.should ==
>      ["user=#{@user.id}", "token=#{@user.activation_token}"].to_set
> where the first line could be replaced, in this case, with:
> Regexp.new('http://test.host/users/continue_register\?([^\s]+)\&([^\s]+)' 
> ).
> although I would prefer another matcher to exist:
>  match(email.body).to_a[1..-1].should have_same_elements(["user=# 
> {@user.id}", "token=#{@user.activation_token}"])
> But the problem with this approach is that I would probably need to  
> write another test for testing this test ;)
>>> How could I verify that the delivered message contains a correct  
>>> url? I know that I should follow the url in an acceptance test,  
>>> but I'm just trying to test that the message is been correct  
>>> generated, in a unit test.
>>> Please, let me know if I missed something conceptually while  
>>> testing this situation.
>>> Thanks for RSpec and the tips about machinist, faker and webrat.
>>> Just one more doubt. When using machinist, is it possible to  
>>> ignore the blueprint while calling 'make' on an ActiveRecord  
>>> class? I had to create a named blueprint reseting all fields set  
>>> by the blueprint.
>> I don't know of a way of to do this, but why do you need to?
> Usually, for most tests, I need to create active users.
> Only for testing the registering process, I need another structure.
> Active users must have login, name, email and password.
> Inactive users would only have email.
>> You may want to look at a couple of other libraries like Fixjour,  
>> Fixture Replacement and Factory Girl - they serve the same function  
>> as Machinist, but don't (afaik) add methods to ActiveRecord::Base.
> Actually, I've taken a look at Factory Girl, but prefered Machinist,  
> which is awesome. I'll take a look at Fixjour and Fixture  
> Replacement. Do they have a blueprint-like feature?
> I have no problem in creating methods in ActiveRecord::Base. I just  
> wanted to call something like:
> User.make_without_blueprint(:email => 'test at test.com', :active =>  
> 'false')

How about make_inactive and put it directly on User?

Also, there may be some sort of named blueprint feature. I'm not in a  
position to look that up right now, but I think I remember that being  
supported. If not, we should make it so :)

> Thank you for your feedback,
> Rodrigo.
> __________________________________________________
> Faça ligações para outros computadores com o novo Yahoo! Messenger http://br.beta.messenger.yahoo.c 
> om/ _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

More information about the rspec-users mailing list