Colorful RSpec Stories

I do not know why there are no colors in the stories, but here is a small monkeypatch to fix this… (setting option[color] and not overwriting them would be better, but i could not find where to set them…)

#vendor/plugins/rspec/lib/spec/runner/formatter/story/plain_text_formatter.rb line ~50
#COLORIZE!
@options.colour = true#HACK
out = "#@count scenarios: #@successful_scenario_count succeeded, #{@failed_scenarios.size} failed, #@pending_scenario_count pending"
if @failed_scenarios.size > 0
  out = red(out)
else 
  if @pending_scenario_count > 0
    out = yellow(out)
  else
    out = green(out)
  end
end
@output.puts out 
#COLORIZE! END
#@output.puts "#@count scenarios: #@successful_scenario_count succeeded, #{@failed_scenarios.size} failed, #@pending_scenario_count pending"

Enjoy the colors 😀

RSpec: Testing Mail Delivery (simple)

To really test mail delivery one needs to send and then grab via smtp, but i do not want to go this far atm. So i wrote a surprisingly simple test, that satisfies my need for coverage and be done with it!

If you messed around with your environment.rb and are not sure wheter emails are send in test mode, just enter a real email and watch the inbox, before sending mass-emails…

#spec/models/user_mailer_spec.rb
describe UserMailer do
  fixtures :users

  before(:each) do
    ActionMailer::Base.deliveries = []
  end

  it 'should send activation' do
    UserMailer.deliver_activation(users(:quentin))
    sent.first.subject.should =~ /has been activated/#correct subject
    sent.first.body.should =~ /#{CFG[:site]}/#url to our site is there
  end

  def sent
    ActionMailer::Base.deliveries
  end
end

Whats my Coverage? (C0 C1 C2 C3 + Path)

100% coverage sounds great, but is it c0, c1, c2, c3 or path coverage ? If you do not know, here is the answer:

  • C0 = every instruction
  • C1 = every branch if(i==1) a; even if there is no actual instruction in the i!=1 path it needs 2 tests
    1. i == 1
    2. i != 1
  • C2 + C3 ~= is every condition inside an ‘if’ is once true and once false (personally, i could not care less…)
  • C4: Path-coverage = every possible path was taken, if(a) x else b; if(c) y requires 4 tests
    1. a true c true
    2. a true c false
    3. a false c true
    4. a false c false

Try to aim for ~95% C0 and ~70% C1. C2 and C3 in my opinion add no value(except that they cover more paths) and C4 is often not possible, since a loop can have infinite paths (go through 1,2,3,4… times).

Testing Validation the DRY Way

UPDATE: Please have a look at valid_attributes

We all know the discussion: should we test simple validation or not ? Here is a great argument for testing even the most simple validation logic.

assert_invalid_attributes User, :login=>[”,nil,’admin’], :email=>[”,nil,’aa’]

Can it get any simpler ?

It goes through all of them and and tests them one by one, giving hepful error messages like:

<User.login> expected to be invalid when set to <admin>

All you need to do is define a @valid_attributes = {:username=>’foo’…} hash in your test setup to test things like ‘password is not username’ and never worry about validations again!

This goes to test/test_helper.rb or spec/spec_helper.rb

  #create a set of invalid attributes
def invalid_attributes search='', replace=''
  @valid_attributes ||= {}
  @valid_attributes[search]=replace unless search.blank?
  @valid_attributes
end

#idea: http://www.railsforum.com/viewtopic.php?id=741
#example: User, :login=>['',nil,'admin'], :email=>['',nil,'aa','@','a@','@a']
def assert_invalid_attributes(model_class, attributes)
  attributes.each_pair do |attribute, value|
    assert_invalid_value model_class, attribute, value
  end
end

#idea: http://www.railsforum.com/viewtopic.php?id=741
#example: User, :login, ['',nil,'admin']
def assert_invalid_value(model_class, attribute, value)
  if value.kind_of? Array
     value.each { |v| assert_invalid_value model_class, attribute, v }
  else
    attributes = invalid_attributes(attribute,value)
    record = model_class.new(attributes)
    assert_block "<#{model_class}.#{attribute}> expected to be invalid when set to <#{value}>" do
      record.valid? # Must be called to generate the errors
      record.errors.invalid? attribute
    end
  end
end

Convert Test::Unit to RSpec by Script

Living with Test and Spec at the same time is annoying, so here is a small Howto for conversion using the Test::Unit to RSpec converter.

  1. Change spec/spec_helper.rg ‘config.fixture_path =‘ to test/fixtures OR copy all fixtures from test to spec (svn cp test/fixtures spec/fixtures)
  2. copy old code from test_helper to spec_helper (leave includes outside of Spec::Runner.configure do…)
  3. sudo gem install spec_converter
  4. convert all tests ‘spec_converter
  5. correct syntax errors
  6. search and replace ‘test/xxx’ with ‘spec/xxx’ where neccessary
  7. run ‘rake spec
  8. dance if result == :success

Works very nice for me, just one syntax error to correct, and then everything runs. Not all old asserts will be replaced, but this is not problem, since RSpec can work with Test::Unit assertions.