I started using the RailsApps tutorials.
They are pretty good, but I noticed a glaring flaw: There is only one model, the User model. You don’t just create an app where users do nothing.
So I started working with a couple of them to add another model. I also wanted to be able to test them. I wanted to make Capybara tests to test actions for both authenticated and unauthenticated users, similar to what Hartl does, but with a standard authentication gem. Some of the RailsApps use Cucumber, but I have noticed that most Rails developers are not interested in Cucumber, and it looks like Capybara can do some of the things that people do with Cucumber.
One of the apps uses Devise. I have a Github repo based off of the rails3-devise-rspec-cucumber RailsApps tutorial:
https://github.com/emacadie/rails3-devise-rspec-cucumber/tree/add_book
The “add_book” branch has the authentication tests. The model is called Book.
Note 2015-06-14_12.46.25: Now at https://github.com/EMacAdie/rails_apps_dir/tree/master/rails3-devise-rspec-cucumber – HerokuPinger now at https://github.com/EMacAdie/rails_apps_dir/tree/master/heroku-pinger –
I have another app that uses Omniauth called Heroku Pinger. The “second_attempt” branch has some tests that I think do what I am trying to do:
https://github.com/emacadie/heroku-pinger/tree/second_attempt
People will put up a free app on Heroku, but sometimes when they give out the URL the app will not be very responsive since Heroku puts free applications into a hibernate mode. This app was intended to allow people to have a few apps get pinged every hour. John McCaffrey told me that is not very sportsmanlike since the point of free apps is to not suck up too many resources. He is correct, but I decided to keep going because the real point was to work with OmniAuth, and eventually work with mocking and stubbing tests as well. Besides, you could ping any site, not just a Heroku site.
For the Devise app, it turned out to be pretty simple. I did some googling on how to get a Devise session in Capybara. A lot of the results said you had to mess with a Devise dependency called Warden. They say you need to put the following line in your tests:
include Warden::Test::Helpers
I think that some of that information might be out of date, because I went back and commented out those lines, and the Capybara tests still do what I intend them to do. Perhaps updates to the gems made that unnecessary.
The magic is to get an instance of Capybara::Session and pass it around as you need it. I put the code in spec/support/session_provider.rb:
require "spec_helper" include Capybara::DSL include Capybara::RSpecMatchers class SessionProvider attr_reader :the_session def self.get_session the_session ||= create_session end private def self.create_session user = User.create(:name => "Joe", :email => "alindeman@example.com", :password => "ilovegrapes", :password_confirmation => "ilovegrapes") visit "/users/sign_in" puts "Here is method of visit: #{self.method(:visit).owner} " fill_in "Email", :with => "alindeman@example.com" fill_in "Password", :with => "ilovegrapes" click_button "Sign in" page.should have_content("Signed in successfully.") return page end end
The Capybara::Session is stored in the “page” variable. You can use it like this:
it "gets a page to add book with session provider" do p = SessionProvider.get_session visit "/books/new" # puts "Page is a #{page.class}" page.should have_content("New book") end
I have a slightly more involved test that actually adds a book and checks that the count of Books has increased by 1.
The OmniAuth test took me longer. Part of is I later realized was that I was using methods like “get” from the Controller tests, and not the Capybara “visit” method. Capybara has a “get” method, but for some reason I got nowhere with that. I wasted a lot of time thinking it would work.
The SessionProvider class is in spec/support/session_provider.rb and looks like this:
require "spec_helper" include Capybara::DSL include Capybara::RSpecMatchers class SessionProvider attr_reader :the_session def self.get_session the_session ||= create_session end private def self.create_session OmniAuth.config.test_mode = true OmniAuth.config.mock_auth[:twitter] = { 'uid' => '12345', 'provider' => 'twitter', 'info' => { 'name' => 'Jimmy C' } } @user = FactoryGirl.create(:user) visit '/signin' return page end end
In the tests that use this, you just do this:
it "should contain link to stuff" do p = SessionProvider.get_session visit "/websites" response.body.should have_content('Listing websites') response.body.should have_content('New Website') end
I looked at the OmniAuth wiki and a few posts on StackOverflow and tried and re-tried several times, until I had my “visit” epiphany.
Image from the Sinope Gospels, a 6th-century Byzantine manuscript housed at the Bibliothèque nationale de France (Wikipedia page here), manuscript information here, image from Wikimedia, image assumed allowed under Fair Use.
Great to see you extending the RailsApps applications and working out the complexities. Exactly what I was hoping, that people would get a base to build from and then share more advanced implementations. More power to you!
Thanks for the reply. I think your tutorials are great stuff and really helpful.