Mike Gehard's blog
Lately I've had the opportunity to use Jasmine to test drive a whole bunch of Javascript and am loving it. If you haven't had a chance to take Jasmine for a spin, I recommend you take some time to do so.
When I heard that Rails 3.1 was going to include CoffeeScript I decided to work to figure out how I could write both my production code as well as my specs in Coffeescript.
You may be asking yourself why you'd want to do this in the first place. Well here's why I would want to do it.
We had some Webdriver based Cucumber tests that passed fine locally but kept failing on our CI box. Our CI box is a bit underpowered at the moment so I thought what might be happening is that our tests weren't waiting long enough for the Ajaxy stuff to happen because the Ajax responses were taking a long time.
After some poking around in the source code of jQuery, I found the $.active property. This property keeps track of the number of active Ajax requests that are going on and I thought this might help us out.
What I came up with was this gist:
I added this step right after my Cucumber step that caused the Ajax call so that Cucumber would wait to move on until I knew that everything was done.
This step solved our CI failures and all was good in our test suite again.
There is a new kid on the block when it comes to file attachments for Rails and it is called Carrierwave.
Carrierwave gives you the ability to easily store attachments on S3 using another great gem called Fog.
Uploading files to S3 is great for many reasons but it can slow down your testing environment because it takes a while to send stuff up to S3. The Carrierwave documentation tells you how to switch the storage location over to file storage during testing but that wasn't enough for me. I wanted to use the same storage mechanism for dev, test and production so I sought out a way to do so.
I had heard about Fog's ability to mock itself to pretend that it was interacting with S3 so I decided to see if I could get it working with Carrierwave. This allowed me to use the same storage mechanism in test mode without slowing my tests down waiting for images to go to S3.
After a bunch of tinkering and a message on the Fog mailing list(thanks for the quick response Wesley), this is what I came up with:
The key is that you have to tell the mocked Fog that an S3 bucket exists before it will let Carrierwave put an image there. I wasn't doing this at first and Carrierwave kept showing me a 404 error from Fog.
Drop this in a file in your spec/support and/or features/support directories and you will have your tests thinking they are sending things to S3 without actually sending them to S3.
Now I don't have to mess around with having a bunch of test images laying around my hard drive and I can make sure I'm using the same storage mechanism across all environments without slowing my tests down.
Your mileage may vary but I'd love to hear how this works for people and if there are any limitations. I haven't found any yet.
UPDATE: Check out my guest post on the Engine Yard Blog for updated details: http://www.engineyard.com/blog/2011/mocking-fog-when-using-it-with-carrierwave/
Rails 3 brings with it ActiveModel.
ActiveModel give you a way to make non-db backed models look like db backed models to your views and controllers. See this post for a good explanation of what using ActiveModel buys you.
ActiveModel gives you a great way to test that your class implements the minimum ActiveModel interface: ActiveModel::Lint::Tests
Check out this gist for the details for using these tests in RSpec:
Forget the steps I published earlier...just install the capybara-firebug gem and away you go.
Thanks jfirebaugh!
If you haven't looked at OmniAuth for authentication with sites like Google, Github and Facebook, then you should take a look. It is pretty killer.
This morning we needed to write a Cucumber scenario to test that a user could log into the system using Google Apps.
We did a quick spike on getting OminAuth integrated, which was a super simple process, and poked around in the browser to make sure it was working OK.
Thanks to Jose Valim for providing some guidance, via the Devise test suite, on how to get this all up and running.
The basics can be found in this Gist:
I put that code in /features/support/omniauth.rb and then all I need to do is label any scenarios that need to deal with login with an @omniauth_test tag and we are all set.
As our features count grows, I could see us doing this before/after all Cucumber scenarios.
Note: You need to be using 0.2.0.beta5 of OmniAuth to get this to work. Earlier versions don't have the testing functionality built in.
Also Note: This same functionality can be used in good, old RSpec integration tests or Steak tests as well.
SauceLabs is a cloud based way to test your site against different browsers.
Up until now, they only supported the older Selenium RC based tests.
For those of us using Capybara, we were out of luck because Capybara uses Webdriver.
Well that just changed, they now support Webdriver. Check out the instructions on how to get is set up here.
The one thing that I disagree with in that post is setting the default Capybara driver to :sauce (Capybara.default_driver = :sauce). This seems a little heavy handed to me since I may not want to run all of my scenarios through the Sauce driver.
Upon further review of the source code, it looks like after installing the sauce gem, they redirect any scenarios tagged with @selenium to the Sauce driver. I like this better so if you don't want to switch over all of your scenarios over to Sauce, you can just ignore the line mentioned above and simply tag the scenarios you want to run on Sauce with @selenium.
I haven't had a lot of time to play with this but at least it is a start in getting Capybara based Cucumber scenarios to run against Sauce Labs.
My next step is to figure out how to run one Cucumber scenario to run against multiple browsers on Sauce.
Another thing I'd like to figure out how to do is only run Selenium based tests on demand so they don't run on Sauce every time I run my Cucumber suite. That could run up a decent Sauce bill, especially if you had multiple developers running Cucumber scenarios multiple times a day.
Our first tech talk from our Boulder office is now up. You can check it out here: http://pivotallabs.com/talks/121-riak-overview-and-schema-design
Back in the day, plugins were an acceptable way to extend Rails because gems were hard to create and publish. It was easier to just put up a public repo and have people pull a plugin from there.
In Rails 3, I think that we've got a good solution to these problems. Bundler allows us to type bundle gem and quickly get the skeleton of a gem. Bundler also also allows us to easily pull in unpublished gems via the :git option from any public git repo. The new rubygems.org allows us to easily publish gems that are ready for prime time.
Yes plugin maintainers will need to take a little time to update their plugins to be gems but I think that time is outweighed by the benefits of less code in Rails (because the plugin architecture code will be removed), possibly increased startup time because less code is running and adhernace to a standard way of loading Ruby extensions via the gem mechanism.
What do people think?
A lot of hard work has gone into modularizing the Rails3 codebase to make it easier to work with and faster.
Do we continue the spring cleaning and get rid of other remnants from the past?
In the continued saga that is the Rails 2.3.10 to Rails 3.0.1 upgrade, I found this little nugget today...you want to add a custom action to a controller that inherits from a Devise controller. Here are the steps that you need to follow:
1) Create a custom controller that overrides some out of the box Devise actions as well as adds a new method.
class RegistrationsController < Devise::RegistrationsController
def update
# do something different here
end
def deactivate_owner
# deactivate code here
end
end
2) You have to tell Devise to use the new controller in routes.rb as well as add the new route to the new action.
devise_for :owners, :controllers => { :registrations => "registrations" } do
post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
end
When we initially implemented this, our route definition looked like this:
post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
devise_for :owners, :controllers => { :registrations => "registrations" }
When it was implemented this way, we kept getting a AbstractController::ActionNotFound exception. Once we passed the block to devise_for seen in #2, everything worked fine.
