More Cucumber Common Steps and Love

You just have to love this “syntax” 😀
Story

#features/discussion.feature
Scenario: I add to an discussion
    Given I am logged in
    And a "Discussion" exists for "Festival" "1"
    And I am on "Festival" "1"
    When I fill in "Text" with "Halloo"
    And I press "Send"
    Then I should be on "Festival" "1"
    And I should see "Halloo"

Steps

#features/step_definitions/common_steps.rb 
# On page/record
Given /^I am on "([^"]*)"$/ do |path|
  visit path
end

Then /^I should be on "([^"]*)"$/ do |path|
  current_path.should == path
end

Given /^I am on "([^"]*)" "([^"]*)"$/ do |model,number|
  visit polymorphic_path(record_from_strings(model,number))
end

Then /^I should be on "([^"]*)" "([^"]*)"$/ do |model,number|
   current_path.should == polymorphic_path(record_from_strings(model,number))
end

# Existing
Given /^a "([^"]*)" exists for "([^"]*)" "([^"]*)"$/ do |associated,model,number|
  record = record_from_strings(model,number)
  record.send(associated.underscore+'=',valid(associated))
  record.save!
end


# Login
Then /^I should be logged in$/ do
  should be_logged_in
end

Given /^I am logged in$/ do
  visit 'login'
  fill_in 'email', :with=>'quentin@example.com'
  fill_in 'password', :with=>'test'
  click_button 'Login'
end


# Support
def current_path
  response.request.request_uri
end

def record_from_strings(model,number)
  model.constantize.find(:first,:offset=>number.to_i-1)
end

env.rb

#features/support/env.rb
#load all fixtures
include AuthenticatedTestHelper #restful_authentification
include ValidAttributes #http://github.com/grosser/valid_attributes

Getting Started with Cucumber on Ubuntu

I finally found some time to mess with cucumber, its pretty nice but has some edges and quircks… but for now i like it 🙂

Install
This was rather hard… (these libraries and frex should not be necessary for mac)

sudo apt-get install libxslt1-dev libxml2-dev racc
sudo gem install aaronp-frex brynary-webrat
sudo gem install cucumber
./script/generate cucumber
rake features

First story

#features/signup.feature
Feature: Signup
  Scenario: A new user comes to the page and wants to register.
    Given I am on "/"
    When I click "register"
    And I fill in "user[name]" with "Michael G"
    And I fill in "Email" with "test@test.de"
    And I fill in "Password" with "test"
    And I fill in "Password confirmation" with "test"
    And I press "Register for the free basic account"
    Then I should be on "/"
    And I should be logged in
    And I should see a flash about "activation"

Environment setup

#append to features/support/env.rb
#load all fixtures -- https://pragmatig.wordpress.com/2008/12/25/load-all-fixtures-when-not-in-test/
include AuthenticatedTestHelper # restful authentification
include RspecResponseEnhancer # https://pragmatig.wordpress.com/2008/04/20/rspec-responseshould-information-enhancers/

Common steps
There is a nice collection of steps already available in steps/webrat_steps, but some basics where missing….

#features/step_definitions/common_steps.rb
When /^I click "(.*)"$/ do |link|
  click_link(link)
end

When /^I am on "(.*)"$/ do |path|
  header('Accept-Language','EN-en')
  visit path
end

Then /^I should be on "(.*)"$/ do |path|
  response.request.request_uri.should == path
end

Then /^I should see a flash about "(.*)"/ do |message|
  doc.search('div.flash_notice').inner_html.include?(message).should be_true
end

Then /^I should be logged in$/ do
  should be_logged_in
end

def doc
  Hpricot(response.body)
end

Output

 rake features
Feature: Signup  # features/signup.feature
  Scenario: A new user comes to the page and wants to register.  # features/signup.feature:2
    Given I am on "/"                                            # features/step_definitions/common_steps.rb:6
    When I click register                                        # features/step_definitions/common_steps.rb:1
    And I fill in "user[name]" with "Michael G"            # features/step_definitions/webrat_steps.rb:12
    And I fill in "Email" with "test@test.de"                    # features/step_definitions/webrat_steps.rb:12
    And I fill in "Password" with "test"                         # features/step_definitions/webrat_steps.rb:12
    And I fill in "Password confirmation" with "test"            # features/step_definitions/webrat_steps.rb:12
    And I press "Register for the free basic account"            # features/step_definitions/webrat_steps.rb:4
    Then I should be on "/"                                      # features/step_definitions/common_steps.rb:11
    And I should be logged in                                    # features/step_definitions/common_steps.rb:19
    And I should see a flash about "activation"                  # features/step_definitions/common_steps.rb:15

10 steps passed

Feels great to write stories again, sadly takes more time then unit tests, but the readability is superb! IMO a very nice way of doing integration tests…

Load All Fixtures (when not in test…)

For tests we have the `fixtures :all`. But for other usages fixture loading is idiotically hard…

Fixtures.create_fixtures(folder, file) should just load all when no file is given, but alas we have to do this ourselves…

#Load all fixtures
Fixtures.reset_cache  #only needed when you reload them
fixtures_folder = File.join(RAILS_ROOT, 'spec', 'fixtures')
fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
Fixtures.create_fixtures(fixtures_folder, fixtures)

No More Crontab Madness with a single Night Rake-Task

Crontab grows larger and larger with every new cleanup/maintence task…

...
0 0 1 * * cd ~/apps/my_app/current && RAILS_ENV=production rake summary:weekly_update >> ~/apps/my_app/current/log/summary_weekly_update.log
0 0 7 * * cd ~/apps/my_app/current && RAILS_ENV=production rake summary:weekly_update >> ~/apps/my_app/current/log/summary_weekly_update.log
0 0 14 * * cd ~/apps/my_app/current && RAILS_ENV=production rake summary:weekly_update >> ~/apps/my_app/current/log/summary_weekly_update.log
0 0 21 * * cd ~/apps/my_app/current && RAILS_ENV=production rake summary:weekly_update >> ~/apps/my_app/current/log/summary_weekly_update.log
...
echo 'crontab sucks...'

This is not DRY, so we fix it…

Cron calls “rake night” every night and night takes care of the rest…

#redirect and append puts to the logfile
def logging_to(path)
  FileUtils.touch([path])
  $stdout = File.new(path,'a+')
  yield
  $stdout = STDOUT
end

#use DRY to simulate a dry run
#uses hoptoad for error recording(better than exception notifier...), get it @ hoptoad.com
def invoke_and_log_task(name)
  begin
    logging_to "log/#{name.gsub(':','_')}.log" do
      if ENV['DRY']
        puts "dry-running #{name}"
      else
        Rake::Task[name].invoke
      end
    end
  rescue Exception => e
    HoptoadNotifier.notify(
      :error_class => "Nightly #{name} Error #{e.class}",
      :error_message => "#{e.message}"
    )
  end
end

desc "Runs maintains tasks at night"
task :night do
  invoke_and_log_task 'feed:update'
  invoke_and_log_task 'summary:weekly_update' if [1,7,14,21].include? Time.now.day
  ENV['greeting'] = 'Hello again...'
  invoke_and_log_task 'spam_users' if Time.now.day == 5
end

TAP the Testable, Auto-documenting, Class-based Rake

UPDATE: always use “# YourClassName::manifest some-text” in the comments, or the task will not be found

If you have rake tasks and you work TDD then your doing it wrong!
Use TAP! Its like rake but auto-documenting + testable + awesome!

See the presentation!
See the slides.
See the website!
See the repo!

gem install tap

(If you really want to go crazy with sequenced task and all kinds of logic-task-chaining, this is possible too.)