Rails + Kaminari with RSpec

The default rspec view specs don’t work so well for index pages if you have added Kaminari pagination.

The reason is the assigned variable in the controller is not an array, but is instead expected to respond to additional methods: current_page, num_pages, and limit_value.

Modifying the view spec like below appears to address the issue.

@books = [
  Factory.build(:book),
  Factory.build(:book)
]
@books.stub!(:current_page).and_return(1)
@books.stub!(:num_pages).and_return(1)
@books.stub!(:limit_value).and_return(1)
Advertisements
Posted in rails | Tagged , | Leave a comment

Rails + Devise and Rspec

So I tried using Devise on a project and ran into an issue with the default generated controller specs.

PlansController POST create with invalid params re-renders the 'new' template
Failure/Error: response.should render_template("new")
expecting <"new"> but rendering with <"">
# ./plans_controller_spec.rb:73:in `block (4 levels) in <top (required)>'

PlansController PUT update with invalid params re-renders the 'edit' template
Failure/Error: response.should render_template("edit")
expecting <"edit"> but rendering with <"">
# ./plans_controller_spec.rb:117:in `block (4 levels) in <top (required)>'

The problem is that my application is requiring a valid user here, so the controller is redirecting rather than rendering what is expected.

If you look you’ll find helpful advise on the Devise wiki about how to resolve this issue for your controller specs.

I’m using FactoryGirl, so this is the solution I went with:

$ cat spec/support/controller_macros.rb

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      user = Factory.create(:user)
      # or set a confirmed_at inside the factory. Only necessary if you
      # are using the confirmable module
      user.confirm!
      sign_in user
    end
  end
end

In spec/spec_helper.rb:

RSpec.configure do |config|
...
  config.include Devise::TestHelpers, :type => :controller
  config.extend ControllerMacros, :type => :controller
end

Now what about those integration tests, so-called request specs in rspec parlance. It turns out you need a means of logging in for those specs too.

1) Plans GET /plans works! (now write some real specs)
Failure/Error: response.status.should be(200)

expected #<Fixnum:401> => 200
got #<Fixnum:605> => 302

After quite a bit of googling I came across the following which seems to work great.


$ cat spec/support/request_macros.rb

module RequestMacros
  def login(user)
    page.driver.post user_session_path, 
      :user => {:email => user.email, :password => user.password}
  end
end

In spec/spec_helper.rb:

RSpec.configure do |config|
...
  config.include RequestMacros, :type => :request
end
Posted in rails | Tagged , | 1 Comment

Test a gem with the Rails 3 stack

My previous post covers testing a gem that makes some change to how ActiveRecord works. But what if you want to test a gem that supplies some new behavior to another part of rails, like say ActionController. How do you properly setup that environment without including an entire, mostly blank, rails application into your gem’s sources?

The answer is to essentially do what would normally be done if you were testing inside of a Rails application, by looking at what files get required by your testing framework. I use rspec, and here is my spec_helper.rb.

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

ENV["RAILS_ENV"] ||= 'test'

require 'rubygems'
gem 'actionpack', '>= 3.0.0'
gem 'activesupport', '>= 3.0.0'
gem 'activemodel', '>= 3.0.0'
gem 'railties', '>= 3.0.0'

# Only the parts of rails we want to use
# if you want everything, use "rails/all"
require "action_controller/railtie"
require "rails/test_unit/railtie"

root = File.expand_path(File.dirname(__FILE__))

# Define the application and configuration
module Config
  class Application < ::Rails::Application
    # configuration here if needed
    config.active_support.deprecation = :stderr 
  end
end

# Initialize the application
Config::Application.initialize!

require 'rspec/rails'

RSpec.configure do |config|
end

require 'my_gem'

Due to Rails 3 modularity, we can now include only the parts of it we need for our tests. So instead of requiring “rails/all” you can see me bringing in only action_controller and some test unit helpers.

I banged my head against another problem, which is not visible from the code here. Rails looks for its root directory by looking for a specific file starting with the directory of the file in which you’ve done this configuration. I’ve seen evidence that some code looks for script/rails and other code looks for config.ru. It isn’t necessary for either of these files to have anything in them, but simply to exist as you’d expect in a rails application. I put them in my ‘spec’ directory.

  • spec/config.ru
  • spec/script/rails

I’m testing a gem that does something with controllers, so I also setup spec/config/routes.rb. All of my tests use the same two controllers, so I set those up in spec/app/controllers/.

After all this setup, spec tests work as usual in a Rails application for spec/controllers, spec/routing, etc.

Posted in rails | Tagged , | 1 Comment

How to test a gem that changes ActiveRecord

The code given here comes from my gem, safe_attributes, which can be retrieved from https://github.com/bjones/safe_attributes. Some of the code for setting up this testing environment originally came from or was inspired by octopus, which is also available on github.

The strategy for testing my gem is to test the code against ActiveRecord outside of a Rails environment. A simple google search shows that this is one way in which people use ActiveRecord and it definitely seems like it would be easier to setup. I’ll explore testing a gem that operates with ActionController in a future post.

The following example is for rspec, and is taken as an example of testing a gem that modifies some part of ActiveRecord behavior. This is the content of spec_helper.rb.

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

This code is just ensuring the spec directory and the gem’s lib directory are part of the load path.

require 'rubygems'
gem 'activerecord', '>= 3.0.0'
require 'active_record'
gem 'activesupport', '>= 3.0.0'
require 'active_support'

This should probably be done with bundler, especially given that I’m using bundler already elsewhere. The point here is activerecord is loaded.

require 'safe_attributes'

Require in the gem’s code.

require 'rspec'
require 'rspec/autorun'

Rspec stuff, you need at least the first one here.

root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => "#{root}/db/safeattributes.db"
)

This establishes a connection to a simple sqlite database for testing. There happen to be tasks to setup this test database in the gem’s Rakefile.

RSpec.configure do |config|
end

Finally, configure rspec with defaults.

Here’s a look at the most interesting parts of the safe_attributes_spec.rb, which tests the gem.

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'my_models'")
ActiveRecord::Base.connection.create_table(:my_models) do |t|
    t.string :class
    t.string :bad_attribute
    t.string :good_attribute
end

class MyModel < ActiveRecord::Base
    include SafeAttributes
    bad_attribute_names :bad_attribute, :bad_attribute=
end

describe MyModel do

    before(:each) do
        ActiveRecord::Base.connection.increment_open_transactions
        ActiveRecord::Base.connection.begin_db_transaction
        @model = MyModel.new
    end

    after(:each) do
        ActiveRecord::Base.connection.rollback_db_transaction
        ActiveRecord::Base.connection.decrement_open_transactions
    end

The before/after hooks are used to start a transaction and roll it back after each test case finishes. The table and model are created within the spec itself rather than outside of it, but either way would work.

Thanks to octopus for the inspiration and I hope this helps someone else.

Posted in rails | Tagged , , | 2 Comments

Better legacy database support for Rails

Have you seen these errors?

NoMethodError: undefined method `columns_hash' for "":String
NoMethodError: undefined method `private_method_defined?' for "":String

If you’ve tried using Rails 3 with a legacy database, it is possible you’ve run across issues with some of your columns having the same name as existing methods of instances of ActiveRecord::Base. My primary example of this has been columns with the name ‘class’. If you can’t change the column name to be ‘klass’ or ‘clazz’, then you may want to consider my solution to this issue.

I’ve created a gem called safe_attributes that turns off the creation of the attribute accessors that conflict with existing instance methods of ActiveRecord::Base.

https://github.com/bjones/safe_attributes
https://rubygems.org/gems/safe_attributes

Posted in rails | Tagged | 2 Comments

How to edit ~/.MacOSX/environment.plist from a shell

This post is for me. I’ve wasted too much time trying to figure out how to edit environment.plist by hand from a shell without resorting to XCode’s property editor. The solution is simple, even if a Google search refuses to quickly and easily address the issue.

  1. Open up a terminal.
  2. mkdir ~/.MacOSX; touch ~/.MacOSX/environment.plist
  3. defaults write ~/.MacOSX/environment PATH "/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/local/git/bin:/usr/local/mysql/bin:/opt/local/bin:/opt/local/sbin:/opt/local/lib/postgresql90/bin"

Want to check your work?

defaults read ~/.MacOSX/environment

{
PATH = “/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/local/git/bin:/usr/local/mysql/bin:/opt/local/bin:/opt/local/sbin:/opt/local/lib/postgresql90/bin”;
}

Posted in OSX | Tagged | 1 Comment

Oracle vs Google

From http://news.cnet.com/8301-30684_3-20013546-265.html, I give you this statement by someone unfamiliar with Java and the reasons for other implementations of the technology.

Of course, Java has been forked and fragmented many times over the years, destroying the “write once run anywhere” promise of the technology with different implementations on different computing platforms.

I worked on one such implementation some time ago, GNU Classpath, and the primary reason it came to be was that working with Sun would have required giving up software freedom, our ability to find and fix bugs as we encountered them, and to release those changes as we saw fit. At no point was Classpath interested in fragmenting Java, a big goal was to be 100% compatible.

At some point, the Apache project Harmony, which is what Google uses, came to be via the donations of Intel and IBM. Despite the project name, harmony never came between this project and Classpath. I cannot say how “clean room” the Harmony code is, or how clean the Google VM is. There are obvious performance gains in bytecode that Sun managed to patent thus preventing any other VM from implementing legally.

These things typically end with both sides in a patent sharing agreement to avoid mutually assured destruction, but I guess we’ll have to wait to see how this one turns out. Anyone ready to abolish software patents yet?

Posted in Uncategorized | Leave a comment