DISCLAIMER: This article is by no means related to the recent quarrel about TDD’s death (be it presumed or actual), nor to this article or these hangouts.
Now that we’re clear let us get to the real title:
How I test Rails stuff without loading Rails and learned to use mkdir
Chapter 1: mkdir1
this chapter is quite brief and straightforward…
Starting from Rails 3 every folder you create under app
will be used for autoloading, this means that you can group files by concern2 instead of grouping them by MVC role, or you maybe want to add new “roles”.
For example the app I’m currently working on contains looks like this:
app/
├── admin
├── assets
├── authentication
├── authorization
├── controllers
├── forms
├── helpers
├── journaling
├── mailers
├── models
├── proposals
├── utils
└── views
The journaling
folder contains journal.rb
(a simple Ruby class) and journal_entry
(an ActiveRecord model).
Chapter 2: Rails not required
Talking about that Journal
, it doesn’t really need Rails to be used, it’s ok with any JournalEntry
-ish object that exposes this API:
class FauxJournalEntryClass < Struct.new(:difference, :user, :model_type, :model_id, :action)
def save!
@persisted = true
end
def persisted?
@persisted
end
end
So it can be used and tested without loading rails
, that gives opportunity to have the corresponding spec to give us really fast feedback. Enter a lighter version of our spec_helper
:
# spec/spec_helper.rb
RSpec.configure do |config|
# yada, yada, rspec default config from `rspec --init`
end
along with a speedy spec:
require 'light_spec_helper'
require File.expand_path('../../app/journal/journal', __FILE__)
describe Journal do
# examples here...
end
and have the normal spec_helper
to load the light one:
# spec/spec_helper.rb
require 'light_spec_helper'
# stuff coming from `rails generate rspec:install`...
Chapter 3: Mocking Rails
…without making fun of it
With time i often found myself in the need to add a logger, to check current environment or to know the app’s root from this kind of plain classes.
In a Rails app the obvious choices are Rails.logger
, Rails.env
and Rails.root
. In addition I really can’t stand that File.expand_path
and light_spec_helper
, they’re just ugly.
My solution was to:
- have a faux
Rails
constant that quacks like Rails in all those handy methods - add the most probable load paths into the light
spec_helper
- rename the the rails-ready
spec_helper
torails_helper
and keep the light helper as the default one
Here’s how it looks:
# spec/spec_helper.rb
require 'bundler/setup'
require 'pathname'
unless defined? Rails
module Rails
def self.root
Pathname.new(File.expand_path("#{__dir__}/.."))
end
def self.env
'test'
end
def self.logger
@logger ||= begin
require 'logger'
Logger.new(root.join("log/#{env}.log"))
end
end
def self.cache
nil
end
def self.fake?
true
end
def self.implode!
class << self
%i[fake? cache logger env root implode!].each do |m|
remove_method m
end
end
end
end
end
$:.unshift *Dir[File.expand_path("#{Rails.root}/{app/*,lib}")]
RSpec.configure do |config|
# yada, yada, rspec default config from `rspec --init`
end
Notice anything of interest?
I bet you already guessed what Rails.implode!
does…
Here’s the shiny new rails_helper
:
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
Rails.implode! if Rails.respond_to? :implode!
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join('spec/support/**/*.rb')].each do |f|
f = Pathname(f).relative_path_from(Rails.root.join('spec'))
require f
end
ActiveRecord::Migration.maintain_test_schema! # rails 4.1 only
RSpec.configure do |config|
# stuff coming from `rails generate rspec:install`...
end
Conclusion
The introduction of Spring
in Rails 4 has actually made all this stuff useless, running RSpec through it makes it blazingly fast.
Sorry if I wasted your time and I didn’t even make you smile.
-
I don’t actually use
mkdir
, I know how to use my editor ↩ - Not those concerns ↩
Leave a Reply