Rspec Karma (or How rspec ruined my day)
I'm a RoR developer and recently was working on implementing an image manipulation feature in our app. I've gotten into the habit of TDD and naturally I started out by developing tests for my new class that'll handle the features.
This is what my test looked like in spec/lib/image_publisher_spec.rb
require "spec_helper"
describe ImagePublisher do
let(:ip) { ImagePublisher.new }
describe "#validate" do
#...
end
#...
end
and I put the actual library in lib/image_publisher.rb
class ImagePublisher
attr_accessor #...
def initialize
#...
end
def validate(path)
#...
end
end
After developing some tests and implementing the features I had rspec spec/lib/image_publisher_spec.rb
passing. Confident in my abilities, I ran the whole suite with rspec
and was met with the first shocker of the day. My new test was passing fine but another feature spec I had written earlier was failing at the before (:each)
level with the error:
ActionController::RoutingError:
uninitialized constant AdminDashboardController
To clarify, I'm using Active Admin (which incidentally hasn't been my staunchest ally in my conversion to Rails 4) and spec which started failing suddenly was this:
#spec/features/publish_flow_spec.rb
require "spec_helper"
feature "Publish Flow" do
let(:admin_user) { FactoryGirl.create(:admin_user) }
before(:each) do
sign_in_user admin_user
end
def sign_in_user(user)
visit root_path
fill_in 'admin_user_email', with: user.email
fill_in 'admin_user_password', with: user.password
click_button 'Login'
end
describe "some feature" do
it "does something" do
#...
end
end
end
The failure point was the line visit root_path
and it appears the runtime couldn't find Admin::DashboardController
as it seemed to be interpreting it as AdminDashboardController
. What was weird was that running rspec spec/features/publish_flow_spec.rb
didn't produce any errors and passed like it did before.
Quickly googling the error, I found many users facing the same dilemma and I tried the fixes they offered one by one. Here's my account.
Trying Fix #1
My gem file looked something like this.
source 'https://rubygems.org'
ruby "2.0.0"
gem 'rails', '4.0.0'
gem 'pg'
#...
gem 'activeadmin', :github => 'gregbell/active_admin'
#...
and the routes like this
MyApp::Application.routes.draw do
root :to => 'admin/dashboard#index'
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
end
I moved the root definition above ActiveAdmin's definition but that didn't help. I even tried specifying my name space by config.default_namespace = :admin
even though I was using the default namespace already, but it still didn't stop the old spec from failing.
Trying Fix #2
It seemed it may be an active admin issue, and I tried the fix specified in a related github issue. Sadly, specifying config.cache_classes = true
in my application.rb
didn't resolve the failing test either.
Trying Fix #3, #4, #5....
At this point I'd played around with enabling and disabling various permutations of config variables in application.rb
, test.rb
& active_admin.rb
and after spending a couple of hours, nothing seemed to work. I just couldn't understand why the test would fail when run with the whole suite and not when run independently.
I even tried specifying different routes (which were valid because I could see them at http://localhost:3000/rails/info/routes
) in the sign_in_user
function but they all failed with routing errors.
I also thought maybe the test db was at fault but running rake db:test:prepare
didn't solve anything.
Trying Fix #4
I decided to go back to a working commit which had passed all the tests before I added my new class. Here, the test seemed to work fine when run as a whole. I figured it had something to do with the ImagePublisher
class I had recently created. I moved back to the most recent commit and removed rspec spec/lib/image_publisher_spec.rb
and VOILA, the whole suite is passing again.
What fixed it?
Now I knew that something was wrong with the new test that I had written but couldn't figure out how it was meddling with a completely unrelated test. I decided to go back to the basics and searched for a tutorial on how to write spec tests for a class in the lib
folder.
An example listed at this site gave this example:
require 'spec_helper'
describe "Library object" do
before :all do
lib_obj = [
Book.new("JavaScript: The Good Parts", "Douglas Crockford", :development),
Book.new("Designing with Web Standards", "Jeffrey Zeldman", :design),
Book.new("Don't Make me Think", "Steve Krug", :usability),
Book.new("JavaScript Patterns", "Stoyan Stefanov", :development),
Book.new("Responsive Web Design", "Ethan Marcotte", :design)
]
File.open "books.yml", "w" do |f|
f.write YAML::dump lib_obj
end
end
before :each do
@lib = Library.new "books.yml"
end
end
Hmmm...I don't know what I'm doing different but all I see is that the main describe block name as encapsulated in quotes. So that's what I decided to do:
Diff
-describe ImagePublisher do
+describe "ImagePublisher" do
Ran rspec
and ALL THE TESTS PASSED!!!
Now, I have no clue why rspec was passing the classes' test and failing an unrelated test just because a describe block was named how it should be named. I had other tests named similarly for files in spec/lib
, but the only difference there was that they were inside module folders and named with the namespace of that module, while this test wasn't.
Anyhoo, this weird test fixed my woes, but I'd be glad if someone can tell me what I was doing wrong in the first place by not putting the describe block title in quotes. OR, you can tell me where I can find Occam so I can kill him. Till then, I'll chalk this one up as cosmic karma.
Update: I figured out why all this happened. In the ImagePublisher class, I had extended the string class by doing a "Class String" instead of "String.class_eval", which basically FUBARed any other string functions elsewhere.