Mocking Rails, for SPEED

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.

:trollface:

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 to rails_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.


  1. I don’t actually use mkdir, I know how to use my editor 
  2. Not those concerns 

6 Tips for Full Stack Open Source RubyGems Development

Call me old fashioned but up to really recently I used to write my rubygems the old way: manually. 

You know, the .gemspec, the various directories, everything.

It was kinda boring, and that’s why my contributions to the gems ecosystem became scarcer than ever.

Around last December though I wanted to start working on a new project, ruby-lol (more on it in a new article), and I wanted to have everything people expect on a modern gem:

  • continous integration badge
  • coverage badge
  • gem version badge
  • code quality badge
  • gem dependency badge

The most important thing though was minimizing the effort I had to put in to obtain all of that.

So, without further ado, here’s a list of tips to streamline your gems development:

1. bundle gem your-gem-name

This won’t be big news for anyone, but the best way to start developing a gem is calling

$ bundle gem your-gem-name

from the terminal. It will create a directory with everything you need to start coding in there. Edit the license, the readme, and few fields on the gemspec and you can start happily tapping on your keyboard.

2. use a gem version badge

Once you release your gem (hint: gem build your-gem-name.gemspec && gem push your-gem-name-version.gem) you can add a gem version badge to your readme. It’s pretty simple, open the gem page on rubygems.org (i.e. ruby-lol) and click on the badge link. 

You’ll be redirected to badge.fury.io, where you can pick your favorite choice of markup and paste the badge in your readme file. Commit, push and you’re done 🙂

3. continous integration with travis-ci.org

I am officially in love with Travis. No one can not be in love with a working and easy to use hosted continous integration service that is FREE for open source development. 

So how do we set it up? Easy. Sign in travis-ci.org with your github account and give it access to your public projects. Follow the three simple steps and you’re done.

After you have your build up and running remember to copy and add the badge markup to your readme.

4. easy code metrics with code climate

Code quality is something that usually makes people argue. I know I do a lot. Code quality metrics are even worse. I know people that swear by them, and people that think they’re worthless.

I don’t know where you are with this, but I like showing off the quality of my code, and easily finding weak spots I overlooked.

Code climate is a (pricey) service that is very good at analyzing your code and allowing you to navigate around its issues. I am a paid subscriber, but like many companies they also have a free way to check the code quality of gems.

Their add github repository page is what you’re looking for. 

As for the other services remember, when you’re done, to add the badge to your readme.

5. test coverage, some love it, some hate it

As with code metrics, test coverage is something that at least makes people talk.

I like keeping track of the coverage of my tests/specs, because knowing that everything I have written is covered by tests makes me work with a more relaxed state of mind.

Coveralls.io is another great service, free code coverage for opensource projects, easy to setup, it integrates perfectly with Travis.

Once again, setting it up is easy. Signup with github, give access to public projects, get the badge and put it in your readme.

6. keep up with your dependencies

Last but not least, Gemnasium is a service that allows you to keep track of your gem dependencies and notifies you when you become outdated.

Free for open source projects, you might be familiar with the set up process. Sign in with github, copy the badge when you’re done 🙂

Gemnasium ends the list of our list of tips. If you want, let me know what you think of it and whether there are more easy-to-set-up and useful services. Have fun!