How to test routes with Rspec 2 in Rails 3? - ruby-on-rails-3

I can't find anything explaining how to test routes in Rails 3. Even in the Rspec book, it doesn't explain well.
Thanks

There is a brief example on the rspec-rails Github site. You can also use the scaffold generator to produce some canned examples. For instance,
rails g scaffold Article
should produce something like this:
require "spec_helper"
describe ArticlesController do
describe "routing" do
it "routes to #index" do
get("/articles").should route_to("articles#index")
end
it "routes to #new" do
get("/articles/new").should route_to("articles#new")
end
it "routes to #show" do
get("/articles/1").should route_to("articles#show", :id => "1")
end
it "routes to #edit" do
get("/articles/1/edit").should route_to("articles#edit", :id => "1")
end
it "routes to #create" do
post("/articles").should route_to("articles#create")
end
it "routes to #update" do
put("/articles/1").should route_to("articles#update", :id => "1")
end
it "routes to #destroy" do
delete("/articles/1").should route_to("articles#destroy", :id => "1")
end
end
end

Zetetic's answer explains how to test routes. This answer explains why you shouldn't do that.
In general, your tests should test the behavior exposed to the user (or client object), not the implementation by which that behavior is provided. Routes are user-facing: when the user types in http://www.mysite.com/profile, he doesn't care that it goes to ProfilesController; rather, he cares that he sees his profile.
So don't test that you're going to ProfilesController. Rather, set up a Cucumber scenario to test that when the user goes to /profile, he sees his name and profile info. That's all you need.
Again: don't test your routes. Test your behavior.

Related

New to Rails 4 Testing - Need help getting started (rSpec and Devise)

I'm relatively new to testing and very new to Rails 4 and rSpec. I am trying to test a controller that uses Devise for authentication and I am stuck. All of the examples I can find are for Rails 3.
I'm using Rails 4.0.3, Devise 3.2.3, rSpec 2.14.1 and FactoryGirl 4.4.0.
class LessonPlansController < ApplicationController
before_action :authenticate_user!
# GET /lesson_plans
def index
#lesson_plans = current_user.lesson_plans.to_a
end
.
.
.
private
# Use callbacks to share common setup or constraints between actions.
def set_lesson_plan
#lesson_plan = LessonPlan.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def lesson_plan_params
params[:lesson_plan]
end
def lesson_plan_params
params.require(:lesson_plan).permit(:title, :synopsis)
end
end
Here are my factory definitions: (Maybe I don't need to define user_id in the lesson_plan factory?)
FactoryGirl.define do
factory :user do
sequence( :username ) { |n| "user#{n}" }
sequence( :email ) { |n| "foo#{n}#example.com" }
password 'foobarbaz'
password_confirmation 'foobarbaz'
created_at Time.now
updated_at Time.now
end
end
FactoryGirl.define do
factory :lesson_plan do
user_id 1
title "The French Revolution"
synopsis "Background and events leading up to the French Revolution"
end
end
And the test part is where I get stuck.
describe LessonPlansController do
let(:valid_attributes) { { } }
let(:valid_session) { {} }
# describe "GET index" do
it "assigns all lesson_plans as #lesson_plans" do
user=FactoryGirl.create(:user)
sign_in user
lesson_plan = LessonPlan.create! valid_attributes
get :index, {}, valid_session
assigns(:lesson_plans).should eq([lesson_plan])
end
end
I'm not sure what to put in valid_attributes and valid_session (or if I even need them). The test will get as far as signing in the user, but will fail on creation of the lesson_plan. Admittedly this is the default/generated test for rSpec, but I am not sure how to proceed.
Examples I have seen use a before block to set up the user. I haven't been able to find anything on the Devise wiki page covering how to write basic rSpec tests for a controller that requires the user to be logged in. Any pointers would be greatly appreciated!
"I'm not sure what to put in valid_attributes and valid_session (or if I even need them)."
Well that depends what you're testing for.. Say you're testing validations & want to ensure that a record not be created if x column is set to null... then you could try to specifically create a record with invalid attributes (e.g. column: nil) and expect the result to not return true; maybe you want to ensure that it IS created with valid attributes.
You can btw, use `attributes_for(:factory_name)`` since you're using FactoryGirl. And no you don't necessarily need to specify the user's id in your lesson plan factory; unless you always want it to reference user 1. You can simply reference user with no value. Check out http://everydayrails.com/2012/03/12/testing-series-intro.html and especially parts 3-5 for an introduction to testing with RSPec.. I found this a pretty easy to follow guide when I was getting started.

Few questions about capybara

I've got a few questions about Capybara. And I might as well ask here since the RDOC in the github page for Capybara is great to get it set up and running. But where is the API or list of available methods??
First. Per *_spec.rb file, should scenario only exist once? Or is it fine to have multiple scenario's in one file?
For example, in spec/request/user_spec.rb:
require 'spec_helper'
feature 'User actions' do
background do
data = {
:first_name => 'foo',
:last_name => 'bar',
...
}
user = User.new(data, :as => :user)
user.save
end
scenario 'User can browse home page' do
visit root_path
page.should have_content('Homepage')
end
scenario 'User should not be able to visit the dashboard' do
visit dashboard_root_path
page.should have_content('You are not authorized to access this page.')
end
end
If there is anything wrong with the code structure above, or if there is room for improvement. I am open feedback.
Second. I notice with the code above. If I have config.use_transactional_fixtures = false in spec/spec_helper.rb, it saves the user twice. This means, in my test database / user table, I would have 2 users named 'foo bar'. Is this normal?
Third. I have a form that has an HTML button. When user clicks on this button, jQuery submits the form. How would I test this with Capybara? I don't think click_button "Add" will do the trick.
Fourth. How would I sign in users in Capybara? I am using Devise. Would sign_in User.first do the trick? And would I be able to access current_user in Capybara?
Lastly, if anyone knows any "Getting Started" guides / tutorials on Rspec + Capybara. Please do mention.
I've also switched over to writing request specs ever since i decided that I was no longer liking Cucumber.
ONE) Having multiple scenarios is indeed fine. You get to use all the other great features of rspec, so I would suggest also using contexts as in the code at the bottom.
TWO) This can probably be solved by using the Rspec Set Gem And the Database Cleaner Gem. Also: The Original Rationale for Set
Warning: make sure you set up DatabaseCleaner correctly when you use set. My own setup (which may be a little overkill but is working for me):
config.before(:suite) do
DatabaseCleaner.clean_with :truncation
end
config.before(:all) do
DatabaseCleaner.clean_with :truncation
end
config.after(:all) do
DatabaseCleaner.clean_with :truncation
end
config.after(:suite) do
DatabaseCleaner.clean_with :truncation
end
THREE) yep! click_button "Add" should work! The complete capybara API is useful but took me a while to grok. Of most relevant importance are the actions and rspec matchers.
example:
click_button "Add"
page.should have_content("Successfully Added")
you can narrow the scope with element finders.
FOURTH) Devise provides helpers. there is a sign_in helper. read the dox :). Here's a demo:
feature 'User actions' do
background do
data = {
:first_name => 'foo',
:last_name => 'bar',
...
}
#user = User.new(data, :as => :user)
#user.save
end
context "no user is signed in" do
scenario 'User can browse home page' do
visit root_path
page.should have_content('Homepage')
end
scenario 'User should not be able to visit the dashboard' do
visit dashboard_root_path
page.should have_content('You are not authorized to access this page.')
end
end
context "user is signed in" do
before :each do
sign_in #user
end
[more scenarios]
end
end
ultimately of course, you'd prolly want to split this up into more specific features. Probably have a "Public Navigation" Feature for all the tests that are about guests seeing content, and then a separate feature for a user signing in, etc.
I am not aware of capybara, but a full list of available methods can be found here:
http://rubydoc.info/github/jnicklas/capybara/master#
hope, that helps

Rspec 2 and Rails 3 stubbing / mocking

I am currently in the process of migration to rails 3 from rails 2 in a large application. In our functional specs, we have alot of stuff like this:
#model = Factory :model
#child = Factory :child
Model.stub!(:find).and_return(#model)
Child.stub!(:find).and_return(#child)
...
#child.should_receive(:method).twice
The main issue is that if I let it hit DB and get actual instance of child, real :method makes tests too complex (need two big factories) and slow.
In code we use various ways to get items: find, dynamic finders, etc
#model = Model.find(1)
#child = #model.children.find_by_name(name)
How would you advice to move this logic to rails 3? Any advice on another stubbing/mocking library maybe?
Normally you would mock the model inside controller specs:
Model.stub!(:find).and_return(mock_model('Model'))
Child.stub!(:find).and_return(mock_model('Child'))
However, when you've got gem "rspec-rails", "~> 2.0" in your rails 3 app's Gemfile, then the standard rails scaffold generator will use rspec to generate specs for you, so running rails generate scaffold MyResource will generate some example specs for you.
The following is a lightly annotated version of what rails/rspec will generate for controller specs, so I suppose this should be considered "The RSpec Way".
describe AccountsController do
# Helper method that returns a mocked version of the account model.
def mock_account(stubs={})
(#mock_account ||= mock_model(Account).as_null_object).tap do |account|
account.stub(stubs) unless stubs.empty?
end
end
describe "GET index" do
it "assigns all accounts as #accounts" do
# Pass a block to stub to specify the return value
Account.stub(:all) { [mock_account] }
get :index
# Assertions are also made against the mock
assigns(:accounts).should eq([mock_account])
end
end
describe "GET show" do
it "assigns the requested account as #account" do
Account.stub(:find).with("37") { mock_account }
get :show, :id => "37"
assigns(:account).should be(mock_account)
end
end
describe "GET new" do
it "assigns a new account as #account" do
Account.stub(:new) { mock_account }
get :new
assigns(:account).should be(mock_account)
end
end
end

TDD: Rspec Ruby MongoDB/Ruby Mongo Driver

how can I use TDD with MongoDB as my second database?
Thanks
Edit:
Using Rspec or anything else that allows me to test it.
[Update]
With MongoMapper set up you can easily use the mongodb connection directly
mongodb = MongoMapper.database
collection = mongodb.collection("my_collection")
collection.find.first
=> {"_id"=>BSON::ObjectId('4e43dfc75d1e1e0001000001'), "key1"=>"val1" }
this other SO Q/A is even more direct, using javascript functions like MongoMapper.database.eval(Mongo::Code.new('function(){ return 11 + 6; })
[/update]
I have such a polyglot architecture, some models with postgresql, others as mongo documents. I'm not really sure what you're asking, so I'll jump straight in and post most my configuration here. It includes my hacks, you probably find more beautiful config elsewhere.
I put the setup in a gist
https://gist.github.com/957341
OK, so here's a document with embedded document, then the spec. I wrote the specs one by one, so they're kinda test driven.
class MyDocument
include MongoMapper::Document
key :title, String
key :published_at, Time, :index => true
key :collaborators, Array
many :my_embedded_documents
end
class MyEmbeddedDocument
include MongoMapper::EmbeddedDocument
key :title, String
key :author, String
embedded_in :my_document
end
the spec
require "spec_helper"
describe MyDocument do
before do
#md = MyDocument.create(:title => "Example", :collaborators => ["mongomapper", "rspec", "oma"] )
end
it "should have title" do
found = MyDocument.find(#md.id)
found.title.should == "Example"
end
it "should have two my_documents" do
MyDocument.create
MyDocument.count.should == 2
end
it "should be able to fetch embedded documents" do
#md.my_embedded_documents << MyEmbeddedDocument.new(:title => "The King", :name => "Elvis Presley")
#md.my_embedded_documents.build(:title => "Embedded example", :name => "Embeddo")
#md.save!
MyDocument.where(:title => "Example").first.should == #md #findMyEmbeddedDocument.count.should == 2
end
end
spec_helper.rb
RSpec.configure do |config|
#...
config.after(:each) do
MongoMapper.database.collections.each(&:remove)
end
end
I don't know what you wanted for answers, but I hope this will be of help to somebody.
From what I can gather, it doesn't appear that your app is sticking to the rails MVC paradigm with its use of this secondary database that apparently doesn't store model data.
I would recommend taking the auxiliary parts of the app that depend on mongo and sticking them into a library. You can make this a gem if it makes sense to use it elsewhere. Then, create a testsuite for the logic of the library using standard testing tools, and integrate into your app either with a simple require, or some directives (depending on what it dies and how you intend to use it).

How to spec validates_uniqueness_of in Rspec?

How does one do this? Couldn't find any examples online... (using rspec 2.5.0 & rails 3.0.5)
Found it in shoulda-matchers: http://rubydoc.info/github/thoughtbot/shoulda-matchers/master/frames
before(:each) do
#attr = { :bar => "foobar" }
end
it "should reject duplicate bar" do
Foo.create!(#attr)
duplicate_bar = Foo.new(#attr)
duplicate_bar.should_not be_valid
end
Not sure if this exactly what you are looking for, but you could check the error messages after the save or update
#widget.save
#untested, but this should be close
#widget.errors.full_messages.include?("validation message you are looking for").should be true
But honestly, this is probably not something that you need to test in your unit tests (if that is where you are placing them). You are basically duplicating unit tests that Rails has already done for you. It would be more appropriate to check for the error message in the view in a cucumber integration test.