Inspecting the page content in a Cucumber session
When you need to see the content of a page (i.e. not all the HTML but the relevant text body)
- you can do
pp (html_content)- pp will format the html String human readable pretty printed
- where html_content can be replaced by one of the following commands:
Rails
body or response.body
Capybara:
page.driver.html.contentpage.body
Webrat:
Nokogiri::HTML(response.body).content
The returned strings can be cleaned up by calling .gsub(/^\s*$/, '').squeeze("\n") on them.\
Although this may be useful for d...
RSpec matcher to check if two numbers are the same
You can usually just use the eq matched to compare two numbers:
expect(deal.total).to eq(120)
If the actual value is a BigDecimal, you might have issues when you match it against a Float:
expect(deal.total_price).to eq(1200.99)
In these cases, try matching it against another BigDecimal:
expect(deal.total_price).to eq BigDecimal(1200.99)
If you don't like the syntax, our rspec_candy gem has a matcher that will compare Fixnums (integers), Floats and `BigDecima...
Test that an exception or error page is raised in Capybara
You can use these step definitions:
Then /^I should not see an error$/ do
(200 .. 399).should include(page.status_code)
end
Then /^I should see an error$/ do
(400 .. 599).should include(page.status_code)
end
Note that you need to tag the scenario with @allow-rescue to test that an error is shown like this
@allow-rescue
Scenario: Accessing the admin area requires a login
When I go to the admin area
Then I should see an error
These step definitions will not work for @javascript scena...
Preloaded associations are filtered by conditions on the same table
When you eagerly load an association list using the .include option, and at the same time have a .where on an included table, two things happen:
- Rails tries to load all involved records in a huge single query spanning multiple database tables.
- The preloaded association list is filtered by the
wherecondition, even though you only wanted to use thewherecondition to filter the containing model.
The second case's behavior is mostly unexpected, because pre-loaded associations usually don't care about the circumstances under whi...
Check if two arrays contain the same elements in Ruby, RSpec or Test::Unit
RSpec 1, RSpec 2
To test whether two arrays have the same elements regardless of order, RSpec 1 and 2 give you the =~ matcher:
actual_array.should =~ expected_array
Rspec 3
With RSpec 3's expect syntax you can choose one of these two matchers:
expect(actual_array).to match_array(['1', '2', '3'])
expect(actual_array).to contain_exactly('1', '2', '3')
Note how match_array takes an argument, but contain_exactly takes a list of elements as varargs.
Test::Unit
If y...
Hints for debugging MySQL InnoDB deadlocks
Deadlocks only occur if two transactions in separate threads compete for the same rows in the database. They usually (but not necessarily) only happen when trying to update or otherwise lock several rows in different order.
Solving deadlocks is potentially complicated, so here are a few pointers:
- MySQL should always detect the deadlock right when it happens, and will throw an error to one of the offending threads. This error states the SQL statement that this thread was currently waiting for, and that tried to acquire one of the competin...
How to fix failing controller specs 91% of the time
If your controller spec never reaches your controller code:
-
Make sure you are signed in.
-
Make sure you are actually triggering a request by saying
get :editor something siliar. -
Know that views are not rendered by default for controller specs unless you tell them to (
render_views).
^
describe UsersController do
describe '#edit' do
it 'should work' do
sign_in
get :edit
end
end
enddefine something like this in your spec_helper.rb:
def sign_in(user = User....
Virtual attributes for array fields
When a has_many association basically serves to store a list of associated strings (tags, categories, ...), it can be convenient to represent this association as a string array in the containing model. Here is an example for this pattern from the acts-as-taggable-on gem:
post = Post.last
p post.tag_list # ['foo', 'bar', 'baz']
post.tag_list = ['bam']
p post.tag_list # ['bam']
This string array tag_list is magical in several ways:
- It is read from and written to a `has...
Hunt down that elusive debug message in Ruby
When you just went through a long debug-fest and infested your code with dozens of debug messages, it can be hard to find all those calls to puts and p. This note describes a hack that lets you trace those messages in your code.
Let's say you want to get rid of a console message "foobar". Copy the Undebug class below to config/initializers.rb. In the same initializer, type a line:
Undebug.trace_message('foobar')
Now run tests or whatever you need to do to to trigger that message. The console output should look like this:
...
Don't use migrations to seed default data
Don't insert table rows in a Rails database migration. This will break tests that expect that database to be empty and cause you all sorts of pain.
If you need a place for default application data, use db/seed.rb or put a script into lib/scripts. It won't run automatically, so add a chore story to Pivotal Tracker as a reminder.
Take care when joining and selecting on scopes
Occasionally some complex query must be processed on the database because building thousands of Ruby objects is impracticable.
Many times you would use scope options, like this:
users = User.scoped(
:joins => 'INNER JOIN orders joined_orders ON users.id = joined_orders.user_id',
:conditions => [ 'joined_orders.date BETWEEN ? AND ?', start_date, end_date ],
:select => '*, SUM(joined_orders.amount) AS amount_sum',
:group => 'users.id'
)
You get ActiveRecord objects and you can ask each of them about its `amou...
Use the back button in Cucumber
In order to go back one page in your Cucumber tests, you can use the following step definition for Capybara:
When(/^I go back$/) do
visit page.driver.request.env['HTTP_REFERER']
end
If you're on Webrat, this should work:
When(/^I go back$/) do
visit request.env["HTTP_REFERER"])
end
An improved version of this step is now part of our gem spreewald on Github.
Test a gem in multiple versions of Rails
Plugins (and gems) are typically tested using a complete sample rails application that lives in the spec folder of the plugin. If your gem is supposed to work with multiple versions of Rails, you might want to use to separate apps - one for each rails version.
For best practice examples that give you full coverage with minimal repitition of code, check out our gems has_defaults and assignable_values. In particular, take a look at:
- Multiple `sp...
Test that a select option is selected with Cucumber
This step tests whether a given select option comes preselected in the HTML. There is another step to test that an option is available at all.
Capybara
Then /^"([^"]*)" should be selected for "([^"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector|
with_scope(selector) do
field_labeled(field).find(:xpath, ".//option[@selected = 'selected'][text() = '#{value}']").should be_present
end
end
Webrat
...
Bookmarklet to generate a commit message with Pivotal Tracker story ID and title
For clarity and traceability, your commit messages should include the ID and title of the Pivotal Tracker story you're working on. For example:
[#12345] Add Google Maps to user profiles
Optional further commit messages in the body
Also see Howto: Write a proper git commit message
To quickly generate such commit messages, add a new link "Commit" to your bookmarks and use the following Javascript as the link URL:
javascript:(function() { ...
Aliases for routes
The following initializer provides an :alias => "my_route_name" option to restful routes in your route.rb. This simply makes the same route also available under a different ..._path / ..._url helpers.
For example,
map.resources :notes, :alias => :snippets
Gives you
notes_path, notes_url, new_note_path... #as always
snippets_path, snippets_url, new_snippet_path... #from the alias
Put this into an initializer:
Test that a CSS selector is present with Cucumber
This note describes a Cucumber step definition that lets you test whether or not a CSS selector is present on the site:
Then I should see an element "#sign_in"
But I should not see an element "#sign_out"
Here is the step definition for Capybara:
Then /^I should (not )?see an element "([^"]*)"$/ do |negate, selector|
expectation = negate ? :should_not : :should
page.send(expectation, have_css(selector))
end
Here is the step definition for Webrat:
Then /^I should (not )?see an element "([^"]*)"$/ do |negate...
Bash Cheat Sheet (standard Emacs mode)
-
Ctrl + R Search commands you entered previously. Press Ctrl + R again to search further back, Ctrl + Shift + R searches forward again.
-
Ctrl + W Deletes from the cursor position to the left.
-
Ctrl + _ Undo. Yes, this also works with a German keyboard layout.
-
Ctrl + L Clear screen.
-
Ctrl + D _Close shell. (EOT, just like in many other shells.) Note: if you dove into another shell (e.g. with
sudo su username) you will close it and return to ...
Standalone Cucumber Test Suite
Sometimes you inherit a non Rails or non Rack based web app such as PHP, Perl, Java / JEE, etc. I like using cucumber for functional testing so I put together this project structure to use as a starting point for testing non Ruby web based applications.
Request a non-HTML format in controller specs
If a controller action responds to other formats than HTML (XML, PDF, Excel, JSON, ...), you can reach that code in a controller spec like this:
describe UsersController do
describe '#index' do
it 'should be able to send an excel file' do
# stubs and expectations go here
get :index, :format => 'xls'
end
end
end
Remember that both the :format parameter and the HTTP_ACCEPT header can m...
Migrating to RSpec 2 from RSpec 1
You will need to upgrade to RSpec >= 2 and rspec-rails >= 2 for Rails 3. Here are some hints to get started:
- In RSpec 2 the executable is
rspec, notspec. - RSpec and rspec-rails have been completely refactored internally. All RSpec classes have been renamed from
Spec::SomethingtoRSpec::Something. This also means that everyrequire 'spec/something'must now berequire 'rspec/something'. - In
spec_helper.rb,Spec::Runner.configurebecomesRSpec.configure - It has become really hard to extend specific example groups ...
Getting your e-mails back after upgrading Thunderbird to version 3
If you previously used version 2.x of Thunderbird and upgraded to 3.x (for example through an Ubuntu release upgrade) you might notice that Thunderbird will not show any of your old e-mails or settings.
This results from a different directory being used for storing profiles and configuration.
You can replace the blank profile with your old one like this:
cd ~
mv .thunderbird .thunderbird-invalid
cp -R .mozilla-thunderbird .thunderbird
Upon its next start, Thunderbird brings up the migration wizard introducing you to a few vers...
Force RubyMine to notice file system changes
If you did file operations inside a shell or for example using Nautilus, it can take quite a while until RubyMine takes note of them and updates things like your project tree or its internal file list.
Flushing file system buffers helps you out (run it from a terminal):
sync
This is also possibly via the RubyMine menus: File → Synchronize.
Setting nil values in Machinist blueprints
Take care when trying to set attributes to nil in a blueprint.
Given the following master blueprint:
Story.blueprint do
title
author { User.make }
editor { User.make }
end
This approach will not overwrite/remove the editor defined in the master blueprint:
Story.blueprint(:draft) do
editor nil
end
...whereas this one will (note the lambda):
Story.blueprint(:draft) do
editor { nil }
end