rspec snippets

How to use RSpec and ZenTest in a standalone Ruby project

Tagged rspec, autotest, zentest, standalone, ruby  Languages ruby

First install the RSpec and ZenTest gem:

$ sudo gem install rspec zentest

Next create the spec folder:

$ cd project_folder
$ mkdir spec

Save the following to spec/helper.rb:

$LOAD_PATH.unshift File.dirname(__FILE__) + '/..'

require 'rubygems'
require 'spec'
#require 'spec/rake/spectask' not needed, because ZenTest supports rspec now

Now create spec/transcoder_spec.rb, and add the following test to it:

require File.dirname(__FILE__) + '/helper'
require File.dirname(__FILE__) + '/../transcoder.rb'

context "Transcoder" do
  setup do
    # Setup your stuff here
  end
  
  it "should support 3gp format" do
    Transcoder.convert("me_and_you.3gp").should == true
  end
end

Let's not forget the class we're testing, put this code in lib/transcoder.rb:

class Transcoder
  def initialize
  end
  
  def self.convert(file)
    return true
  end
end

Note that autotest automatically looks for your code in the lib folder.

Now run the test with the zentest command:

autotest

Change your files and autotest will run the test again.

Tip: Read Getting started with Autotest - Continuous Testing and Setting up autotest to use Growl on OSX for more information on how to increase your productivity.

Dynamic RSpec tests using plain old Ruby

Tagged rspec, ruby, validation, trick, tip  Languages ruby

Just happened to stumble upon the following article at caboo.se: Handy dynamic rspec tip.

I immediately found a way of simplifying a test case that involves testing that an ever increasing number of videos can be transcoded:

require File.dirname(__FILE__) + '/helper'
require File.dirname(__FILE__) + '/../lib/transcoder.rb'

context "Transcoder" do
  
  Dir.glob('videos/*').each do |video|
    it "should support #{video}" do
      Transcoder.convert(video, "site/#{video}.flv")[:file_size].should > 0
    end
  end
end

You can also use it to test model validations:

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


describe "Product" do
  @@valid_product_attributes = { 
      :name         => 'WTF',
      :description  => 'LOL',
      :price        => '10.0',
      :tax          => '22.0'  }

  
  before(:each) do

  end

  it "should create a new instance given valid attributes" do
    Product.create!(@@valid_product_attributes)
  end
  
  @@valid_product_attributes.each do |name, value|
    it "should not allow blank #{name}" do
      lambda do
        Product.create!(@@valid_product_attributes.except(name))
      end.should raise_error(ActiveRecord::RecordInvalid)
    end
  end
end

How to test Rails routing with RSpec

Tagged testing, rspec, routing, routes  Languages ruby
route_for(:controller => 'categories', :action => 'show', :permalink => ['one', 'two', 'three']).should.equal '/one/two/three'
route_for(:controller => 'categories', :action => 'index').should.equal '/'
      
params_from(:get, "/").should.equal {:controller => "categories", :action => "index"}
params_from(:get, "/something/").should.equal {:controller => "categories", :action => "show", :permalink => ["something"]}

The test_spec_on_rails plugin also allow you to test routing and much more, see the docs for more information.

How to test a Sinatra webapp with Rspec

Tagged sinatra, rspec, spec, testing, bdd  Languages ruby

Create spec/app_spec.rb and put the following in it:

require 'sinatra'
require 'spec/interop/test'
require 'sinatra/test/unit'

require 'app'
require 'test/spec'

set :environment, :test

describe 'The HelloWorld App' do
  before(:each) do
    set :views => File.join(File.dirname(__FILE__), "..", "views")
  end

  it "says hello" do
    get '/'
    response.should.be.ok
    response.body.should.equal 'Hello World'
  end
end

Then run the test:

spec spec/app_spec.rb

How to run only one specific test with Rspec

Tagged rspec, focus, guard  Languages ruby

Focusing on one or more tests with Rspec

You can tell Rspec to run only tests tagged with "focus: true" by running Rspec with the --tag focus switch:

rspec ./spec/controllers/concerns/session_expiry_spec.rb --tag focus

This would run e.g. only this test which is marked with focus: true

it "resets session if session has expired", focus: true do
...
end

Integrating with Guard

Use this configuration to integrate this workflow with Guard:

guard 'rspec', cli: '--tag focus --format d --color', all_after_pass: false, all_on_start: false do
...
end

We also want to run all tests when there are no focused tests. We therefore add this to spec/spec_helper.rb:

RSpec.configure do |config|
  # This is dangerous with CI integration e.g. Jenkins
  # config.filter_run focus: true
  config.run_all_when_everything_filtered = true
end

Rspec configuration file

One alternative is to enable the focus option in the Rspec configuration file:

RSpec.configure do |config|
  config.filter_run :focus => true
  config.run_all_when_everything_filtered = true

However, this would be dangerous when running tests with e.g. Jenkins and when someone forgets to remove "focus: true".

How to use fswatch to run a command when a file is changed

Tagged fswatch, rspec  Languages bash

This example executes the the rspec command when a Ruby file is modified in the current directory:

# watch .rb files in spec directory and execute rspec <file>
fswatch -0 -e ".*" -i ".rb" . | xargs -0 -n 1 -I {} rspec {}

Note that fswatch will execute test concurrently if the same test file is changed again before the previous test has time to complete.

Testing Whenever with Rspec

Tagged cron, rspec, whenever  Languages ruby
require 'spec_helper'
require 'whenever'

describe Whenever do
  def gen_cron(stage:)
    x = Whenever::JobList.new(
      file: Rails.root.join("config", "schedule.rb").to_s,
      set: "stage=#{stage}"
    ).generate_cron_output
    x.gsub(Dir.pwd, "")
  end

  it "generates correct cron configuration for each stage" do
    [ :qa, :production ].each do |stage|
      expected = File.read("spec/fixtures/#{stage}.cron")
      cron = gen_cron(stage: stage)
      # NOTE: Uncomment to update expected cron configuration
      #File.open("spec/fixtures/#{stage}.cron", "w") {|x| x << cron }
      assert_equal expected.strip, cron.strip
    end
  end
end