Rails / Testing
From WhyNotWiki
For general Ruby testing, check out Ruby / Testing
Testing in Rails edit (Category edit)
Rails / Testing edit (Category edit)
[edit] Information
Crossing borders: Testing in integrated frameworks, Part 1
Crossing borders: Testing in integrated frameworks, Part 2
[edit] Fixtures
[edit] Using mocks instead of fixtures
Do you hate fixtures? Do they just feel wrong to you? They do to me sometimes. Anyway, this guy, "wilson", gives us an alternative perhaps...
http://metaclass.org/2006/12/22/making-a-mockery-of-activerecord
The traditional Rails solution to testing this would be to make fixtures for all of the above models, and any join models. You would then load and roll back those fixtures numerous times, repetitively testing what you already know; namely, that your database service has been started properly.
This has a number of drawbacks, not least of which is that you are probably referring back to your fixtures as you write your tests, to make sure you get the names right, and that you pick the appropriate fixture for each scenario.
Code sample [1], using the mock_model helper that he wrote:
module MessageSetup
def setup_message
mock_model :campaign
mock_model :person do |m|
m.should_receive(:campaigns).and_return([@campaign])
end
mock_model :list do |m|
m.should_receive(:people).and_return([@person])
end
@message = Message.new :name => "Hello, world", :campaign => @campaign
end
end
context "A Message being generated without include_subscribers" do
include MessageSetup
setup { setup_message }
specify "should create EmailMessages but not Subscriptions" do
@message.should_receive(:lists).and_return([@list])
@message.should_receive(:campaign).and_return(@campaign)
@message.generate.should == 1
@message.should_have(1).email_messages
end
end
[edit] Fixture references
| Homepage: | http://wiki.pluginaweek.org/Fixture_references Announcement |
|---|---|
| Source code: | http://svn.pluginaweek.org/trunk/plugins/active_record/migrations/fixture_references |
| Project/Development: | http://dev.pluginaweek.org/browser/trunk/plugins/active_record/migrations/fixture_references
|
| Description: | fixture_references adds support for referencing external fixtures (by name) from within a fixture definition.
|
departments.yml:
<% fixtures :employees id = 0 %> packaging: id: <%= id += 1 %> name: Packaging manager_id: <%= employees(:bob)['id'] %> quality_assurance: id: <%= id += 1 %> name: Quality Assurance manager_id: <%= employees(:joe)['id'] %>
This is much better than this:
packaging: id: 1 name: Packaging manager_id: 1 quality_assurance: id: 2 name: Quality Assurance manager_id: 2
, which [duplicates (category)] the employee's ID number, which was already defined in employees.yml:
bob: id: 1 name: Bob joe: id: 2 name: Joe
Sure, I guess you could say that we're duplicating the employee's name whenever we say employees(:bob), but referring to a record by its name is much more readable and maintainable than referring to some completely arbitrary ID number!
[edit] [Fixtures (category)]: FixtureScenarios
http://rubyfurnace.com/plugins/fixturescenarios
This plugin allows you to create "scenarios” which are collections of fixtures and ruby files that represent a context against which you can run tests.
- Repository Path: svn://svn.cube6media.com/fixture_scenarios/trunk
Rails / Testing edit (Category edit)
[edit] What if the name of my model is different than the name of my table?
http://wiki.rubyonrails.org/rails/pages/FlexibleFixtures
- Rather than assume your fixture files, database tables, model class names and fixture group names all match in every case, this fixture module will let you define multiple fixtures for the same table, or give you the flexibility to use alternate fixture files in your tests.
http://dev.rubyonrails.org/ticket/1911 flexible fixtures
- fixture :additional_topics, :table_name => "topics"
[edit] Don't like fixtures?
Sometimes I don't.
http://blog.floehopper.org/articles/2006/08/10/fed-up-with-rails-fixtures-part-one Fed up with Rails fixtures? (part one)
[edit] Don't write them by hand; Dump the contents of a database into YAML (fixture) files!
"I want to use the data in my database! I'm sick of manually copying the data into a fixture."
Me too.
http://www.bigbold.com/snippets/posts/show/2525 (Download)
Also:
http://www.jamesbritt.com/index.rb/Development@Rails_Fixtures_from_Database_Contents.txt
Dumping database to YAML fixtures
[edit] How are functional tests and integration tests different?
From Rdoc: "An IntegrationTest is one that spans multiple controllers and actions, tying them all together to ensure they work together as expected. It tests more completely than either unit or functional tests do, exercising the entire stack, from the dispatcher to the database."
"Note that each of the tests shown above test multiple requests. Most functional tests only test one." Also, integration tests run through the entire stack, from the dispatcher, through the routes, into the controller and back. Functional tests skip straight to the controller." http://weblog.jamisbuck.org/2006/3/9/integration-testing-in-rails-1-1
[edit] Common to Functional/Integration tests
After a request, you can inspect/test against "session" like you are used to calling in a controller. (module TestProcess #session just returns @response.session)
[edit] Confusing: functional tests and integration tests are incorrectly named
[edit] "Integration" tests are really acceptance tests
Luke Redpath on http://www.sitepoint.com/forums/showthread.php?t=354597 recommends that Rails rename their tests accordingly. I agree.
[edit] "Functional" tests are really unit tests for controllers
As Jon Tirsen commented on [2]: "Jamis, these "integration" tests are really nice and I think they hit a sweet spot in between exercising a large portion of your application without requiring too much testing infrastructure. These tests are certainly more functional tests than the so called functional tests are! (They are actually unit tests for a controller.)" That's right, the so-called "functional tests" can almost be considered unit tests for controllers.
Luke Redpath on http://www.sitepoint.com/forums/showthread.php?t=354597 recommends that Rails rename their tests accordingly. I agree.
[edit] Testing content/tags on the page
(Common to functional and integration tests?)
[edit] assert_tag
Don't use it. It's deprecated in 1.2 in favor of assert_select.
[edit] assert_select
This is in edge Rails (2007-05-04 16:50). If you're not using edge Rails, you can simply install the plugin:
| Source code: | http://labnotes.org/svn/public/ruby/rails_plugins/assert_select/
|
|---|---|
| As listed in other directories: | http://agilewebdevelopment.com/plugins/assert_select |
| Description: | assert_select helps you write functional tests using CSS selectors.
|
| Authors: | Assaf Arkin
|
http://agilewebdevelopment.com/plugins/assert_select
If you’re using HTML and CSS, you’re already familiar with CSS selectors. CSS selectors have a simple syntax that lets you pick parts of the page and apply styling. assert_select uses the same simple syntax, for testing the content of the page.
You can test that selected element(s) exist, has specific text content, test number and order of elements, and a few more tricks.
Here’s a simple example that asserts the page has the right title:
det test_page_has_right_title get :index assert_select "title", "Welcome" end
[edit] Examples
http://agilewebdevelopment.com/plugins/assert_select
# Form includes four input fields
assert_select "form input", 4
# Page does not have any forms in it.
assert_select "form", false, "Page must contain no forms"
# Page has one link back to user's page.
assert_select "a[href=?]", url_for(:controller=>"user", :id=>user_id),
:count=>1, :text=>"Back to page"
assert_select "form[action=http://test.host/login]" do
assert_select "input[name=username]"
assert_select "input[name=password]"
end
assert_select "div#notice", flash[:notice] || false
http://labnotes.org/svn/public/ruby/rails_plugins/assert_select/cheat/assert_select.html
assert_select "html:root>head>title", "Login" assert_select "form[action=?]", url_for(:action=>"login") do assert_select "input[type=text][name=username]" assert_select "input[type=password][name=password]" end
[edit] Reference / Cheat sheet
http://blog.labnotes.org/2006/09/04/assert_select-cheat-sheet/
[edit] Selectors
- any element
- E
- an element of type E
- E.warning
- an E element whose class is "warning" (the document language specifies how class is determined).
- E#myid
- an E element with ID equal to "myid".
- E[foo]
- an E element with a "foo" attribute
- E[foo="bar"]
- an E element whose "foo" attribute value is exactly equal to "bar"
- E[foo~="bar"]
- an E element whose "foo" attribute value is a list of space-separated values, one of which is exactly equal to "bar"
- E[foo^="bar"]
- an E element whose "foo" attribute value begins exactly with the string "bar"
- E[foo$="bar"]
- an E element whose "foo" attribute value ends exactly with the string "bar"
- E[foo*="bar"]
- an E element whose "foo" attribute value contains the substring "bar"
- E[hreflang|="en"]
- an E element whose "hreflang" attribute has a hyphen-separated list of values beginning (from the left) with "en"
- :root
- an E element, root of the document
- :nth-child(n)
- an E element, the n-th child of its parent
- :nth-last-child(n)
- an E element, the n-th child of its parent, counting from the last one
- :nth-of-type(n)
- an E element, the n-th sibling of its type
- :nth-last-of-type(n)
- an E element, the n-th sibling of its type, counting from the last one
- :first-child
- an E element, first child of its parent
- :last-child
- an E element, last child of its parent
- :first-of-type
- an E element, first sibling of its type
- :last-of-type
- an E element, last sibling of its type
- :only-child
- an E element, only child of its parent
- :only-of-type
- an E element, only sibling of its type
- :empty
- an E element that has no children (including text nodes)
- E
- not(s)
- an E element that does not match simple selector s
- E F
- an F element descendant of an E element
- E > F
- an F element child of an E element
- E + F
- an F element immediately preceded by an E element
- E ~ F
- an F element preceded by an E element
[edit] Substitution Values
- .?
- Class name
- #?
- ID attribute
- [foo=?]
- Attribute value
May be string or regular expression, e.g. "[foo=?]", /bar/i. assert_select Cheat Sheet
[edit] Methods
assert_select(selector, *values, equality?, message?) { |elems| ... }
assert_select(element, selector, *values, equality?, message?) { |elems| ... }
Use selector to select elements from response page or first argument (element), and evalute equality test. Raises exception with message if equality tests fail.
Equality tests include:
- true
- At least one element found (:minimum=>1)
- false
- No element found (:count=>0)
text, :text=>text- All elements found have the text contents (string or regexp)
n, :count=>n- Exactly n elements found
:minimum=>n- At least n elements found
:maximum=>n- At most n elements found
- n..m
- Between n and m elements found
If no count specified, default is :minimum=>1.
With block, calls block with all selected elements. Calling assert_select (or any of the other functions) within that block operates on element selected by outer block.
assert_select_rjs(id?) { |elems| ... } assert_select_rjs(statement, id?) { |elems| ... } assert_select_rjs(:insert, position, id?) { |elems| ... }Asserts that RJS statement updates/inserts HTML content and allows nested assertions on the content.
With id, selects only RJS statement affecting elements with that id. With statement, RJS statements that :replace, :replace_html or :insert. With :insert can limit position (:before, :after, etc).
assert_select_email() { |elems| ... }
Assertions on the (HTML) body of the delivered e-mail.
assert_select_encoded(element?) { |elems| ... }
For operating on encoded HTML (e.g. RSS item description).
css_select(selector, *values) => array css_select(element, selector, *values) => arrayReturns an array with selected elements (empty if no elements selected).
[edit] Comparison with assert_tag
http://agilewebdevelopment.com/plugins/assert_select, iain 29 Oct 2006
assert_tag \ :tag => "div", :ancestor => { :tag => "ul" }, :parent => :tag => "li", :attributes => { :class => "enum" } }, :descendant => { :tag => "span", :child => /hello world/ }which is equivalent to:
assert_select "ul li.enum > div span", /hello world/
Wow! Much more concise.
[edit] Functional tests
[edit] Things to keep in mind
- You're essentially dealing with a lot of mock objects here -- TestSession instead of CGI::Session, for example. Although the mocks behave as the real ones do in most cases (hurray for duck typing), there are exceptions. Watch out for them.
[edit] How to make page requests
post :whatever_action
Don't be tempted to do it like this!
post 'controller_name/whatever_action'
When I've done that by mistake once, it caused at least an hour of debugging confusion, because it was getting the view/template okay and I could see it when I did @request.body, but it was not calling the controller action at all! Weird!
So keep in mind that functional tests already know what controller they're for, so you don't need to specify it (and can get into trouble if you do) when you call get/post.
[edit] How do I pass params to the request?
post :action_name, { :param1 => 'value', ... }
[edit] How do I set the session?
I think you can do this (?):
post :action_name, { :param1 => 'value', ... }, { :session_key_1 => 'value, ... }
Otherwise, try messing with @request.session . In fact, that's the way you have to do it if you want to set up some common session settings in your test case's setup method.
def setup
...
@request.session[:key] = 'value'
...
end
[edit] Problem/Caveat: If you use a custom ActiveRecord session store and try to refer to session.some_column in your functional tests, you'll be disappointed when you get an error
Why? Because in the functional tests, @response.session is of class ActionController::TestSession, not of class [whatever your custom class is], so it doesn't have your model's columns, associations, etc.
Researching a workaround... Currently the best workaround I've heard of is to just use Integration tests, which do use your real sessions -- because they initialize the whole Rails stack.
A closely related problem for those who don't have a custom ActiveRecord session store per se but are trying to use ActiveRecordStore's model attribute: http://dev.rubyonrails.org/ticket/4424
[edit] Integration tests
[edit] How to create an integration test
$ ./script/generate integration_test a
exists test/integration/
create test/integration/a_test.rb
Good example of an integration test: http://weblog.jamisbuck.org/2006/3/9/integration-testing-in-rails-1-1
[edit] What helper methods are available?
response(response.body)session
[edit] [Troubleshooting (category)]: Expected response to be a <:redirect>, but was <500>
Or: "What to do when you get a 500 error"
Unlike in a unit or functional test where it will print out the error on the screen with a nice backtrace so you can quickly see exactly what went wrong (and what file and line to jump to in order to investigate further), in an integration test, it doesn't display a whole lot of information about what went wrong.
It's kind of (a lot) like if this application were deployed on a production site and one of your customers/visitors visited the page. What would they see if your application had a Ruby error? They would probably see a generic error page. That's basically what you're seeing right now: the generic error page for a 500 ("Application error") error.
So what do you do?
Either of these works well:
- In your test (before the assertion that failed), put this code:
puts response.body. That will print out the familiar rescue_action_locally error page with the backtrace, extracted source from the template (if any), parameters, etc. - Look in your
log/test.logfile. The error (and backtace, and params, and date/time of request, etc.) will be logged there as well.
[edit] Setting sessions
[This section needs to be reviewed to see if it is still accurate. It may have changed in recent versions. Or maybe we were just using a custom session store...?]
Setting sessions in integration tests is different than setting them in functional tests:
- Access the session through the
sessionmethod, not through @request.session like you would in a functional test... right?
@request.session (and plain old session, which is an alias for that) will be nil until your first request, after which time it will be a CGI::Session.Actually, I think @request is nil, so @request.session is nil.session!
#puts session.inspect # this will not work! You need to do a page request first to initialize your session!
get '/test/page1'
puts session.inspect
What if you really need to set up the session before your first page request? Well, you can always make a dummy controller/action and call it. That way, @request.session will be initialized but whatever side-effect of calling the real-action-you-want-to-call will be avoided.
get '/dummy/' @request.session[:whatever] = true @request.session.update get '/real/request'
- Question: Will setting @request.session cause the next request you make to have those changes?
No, not unless you save the session (I'm assuming you're using ActiveRecordStore or similar, but it's probably the same regardless).
get '/dummy/' @request.session[:whatever] = true @request.session.update get '/real/request' /usr/lib/ruby/1.8/cgi/session.rb: 315 # Store session data on the server. For some session storage types, 316 # this is a no-op. 317 def update 318 @dbman.update 319 end
[edit] [Caveat (category)]: When using transactional fixtures, if you try to directly query the database after doing an insert, your new row will not be there
I suppose this is to be expected. It's just the way transactions work: the insert doesn't actually take place until the commit. And in the case of transactional fixtures in tests, the commit never happens; at the end of the test, everything is rolled back.
Question: So how do the models continue to work as if that row in the database actually exists??
After my
create, I can inspect my model and see@new_record=false. Not only that, but when I do amodel.reload(which should force it to reload the data from the database), it works, even though the row isn't actually in the table.How does it do this? Does it cheat? By checking if we're in a test and using transactional fixtures when we call
reloadand if so, not actually doing the reload/select?Or maybe there's something about transactions that I'm not understanding...
When I put a "direct query" in there, immediately following the reload, like this:
puts `mysql --host=localhost --user=_ --password='_' db_name -e 'select * from things;'`, it comes up with an empty set.
So is it possible that that query is running "outside of" the transaction and doesn't have access to the still-uncommitted-inserts, and the model code is running "inside" the transaction so it does have access to the inserts?
Ah hah! That's it! It's a different connection to the database, and transactions are per-connection. So everything in ActiveRecord will be inside the transaction, but when I broke out with my [system call] (
`mysql....`), it opened up a new connection, which knows nothing about what what's going on in the transaction until that transaction gets committed (which it never does). It all makes sense now.
[edit] Testing plugins
http://www.pluginaweek.org/2006/11/24/plugin-tip-of-the-week-testing-your-plugins-the-right-way/
Very nice article.
They suggest that you make a tiny Rails application in a subdirectory of your plugin and tell your tests to use that.
It takes a bit of "hacking" your test_helper and stuff to load the Rails environment from your subdirectory and have it find your plugin even though your plugin isn't in vendor/plugins, but ... it works, I guess.
Only other way I can think to do this (be able to test your plugin without installing it in a specific application) is to distribute your plugin inside of a dummy/demo app... And I don't like that option as well, because I think the plugin directory should be the top-level directory for the plugin project.
I've created a plugin template that shows how to create a plugin that includes a test application inside of it.
It is located here: http://svn.tylerrick.com/public/rails/plugins/plugin_with_test_app_template/
Instructions for using the template are available at: http://svn.tylerrick.com/public/rails/plugins/plugin_with_test_app_template/Readme
[edit] Testing migrations
[Rails / Migrations (category)]
Test Your Migrations (http://blog.labnotes.org/2006/07/24/test-your-migrations/) (July 24th, 2006).
...I wouldn’t trust my migrations to just work, not when it comes to live data in production. In fact, I just discovered two bugs in mine.
...
But it failed when doing a dry-run on the production database. I wouldn’t catch it in time without doing the dry-run test.
...
The migration starts by requiring the User model. Then changes the database schema, and then attempts to save new User objects. Because the database schema changes after the User model is loaded, the saving fails without reporting an error. It looks like it works, but there’s no change in the database.
The solution was easy, one line of code courtesy of script/console. After changing the database schema, I force all classes to reload by calling Dispatcher.reset_application!.
Note to self: Always use Dispatcher.reset_application! if you’re changing the database schema and then changing the data.
Fortunately, I discovered both bugs by doing a dry-run against the production database. The migration starts by loading objects into memory, then runs the migration against the database, and tests these objects against the database, printing discrepancies to the console.
I back up the affected tables, run the migration, check the console. If there’s no output, I’m good to go. If I see any errors, I switch back to the old table and fix the migration code.
That's what you call a dry run?? Backing up the data, trying the migration, and then restoring from backup if it fails? That doesn't sound like a dry run to me (a dry run would be if it didn't actually modify the data). But it still sounds like a good idea, to back up your production data before migrating it... And to verify that your migration did what you intended it to rather than just assuming it did...
Test Your Migrations (http://blog.labnotes.org/2006/07/24/test-your-migrations/) (July 24th, 2006).
This also brings me to another topic. Using migrations as the database definition:
- There’s a downside to migrations. Over time, your schema definition will be spread across a number of separate migration files, with many files potentially affecting the definition of each table in your schema. When this happens, it becomes difficult to see exactly what each table contains.
I don’t. I treat migrations like patches. You write them to change the existing behavior, and apply the patch to get a new version. And you can’t always go back. If I’m splitting a table to allow multiple values, I can’t switch back without losing the new data.
The patch is the difference, but the current state is in the latest snapshot. That’s what rake db:structure:dump is for. The migration is just code I can test in development environment, and patch the production environment with the confidence it will work.
[edit] Available assertions
Rails / Testing / Assertions edit
assert_dom_equal assert_dom_not_equal assert_generates assert_no_tag assert_recognizes assert_redirected_to assert_response assert_routing assert_tag assert_template assert_valid
[edit] Libraries
[edit]
ActiveTest
| Rails-style testing
|
|
| Homepage: | http://www.mathewabonyi.com/articles/2006/08/14/activetest-rails-style-testing |
| Source code: | http://mabs29.googlecode.com/svn/trunk/plugins/active_test
|
| As listed in other directories: | http://rubyfurnace.com/plugins/active_test
|
http://rubyfurnace.com/plugins/active_test
ActiveTest wraps Test::Unit in a Rails-like mould. It does a number of things which would be madness to write for a single project and has caused the author of this plugin considerable madness ‘for the fun of it’. Here are a few of its features:
- protects you from the disasters of monkey patching
- fixes issues such as setup/teardown nesting
- allows inheritance of test cases without hacking Test::Unit, et al.
- provides a highly extensible, flexible design—forget modifying Test::Unit::TestCase
- provides class-level macros, a la ActiveRecord, for standard test cases
- brings design (should do x for y because z) slightly closer to test cases
- provides instance-level assertions and helper methods
- provides default specifications for the major forms of testing in Rails
- follows Convention over Configuration, but allows both
- generally makes testing pleasurable
If you do nothing more than install ActiveTest and have all your tests inherit from ActiveTest::Base (not recommended, use one of the Subjects of ActiveTest::Subject itself), you will receive a few benefits without drawbacks:
- setup and teardown are nested
- all setups/teardowns are executed without super
- classes inheriting from ActiveTest::Base can be subclassed without detriment
- anything in the namespace of ActiveTest will not be run by Test::Unit
- in all other respects behaves exactly like Test::Unit::TestCase
...
Base is extended by ActiveTest::Subject, the abstract class for Subjects. Specific sets of ActiveTest::Asserts are mixed into each pre-defined Subject, giving a small set of assertions and convenience methods. Each Subject also has a number of ‘behaviours’ which they can test. These are the macros which can be called through dynamic class methods, such as succeeds_on :index.
There is clean distinction between instance and class methods. All class methods on a Subject are behaviours. All instance methods are assertions (e.g. assert_difference), actions (e.g. index_items or find_all), test cases, or helping methods.
The class methods are your metalanguage. If you learn the few patterns for each Subject, test-driven design will become a breeze.
...
class ExampleControllerTest < ActiveTest::Controller setup succeeds_on :index succeeds_on :show end class ExampleTest < ActiveTest::Model end...
Provides a rake task,
activetest:agiledox, which creates an agiledox file for you after loading up all the tests and libraries and scannig each class for the methods which have been created.
[edit] HttpTest: HTML validation and link checking
http://rubyfurnace.com/plugins/http_test
A plugin for HTML validation and link checking over HTTP. Can be used for monitoring of production servers as well as acceptance testing against staging and development servers.
Command-line:
- ./vendor/plugins/http_test/script/validate http://my.blog.com—validators tidy,xmllint
Add validation to your controller or integration tests:
- assert_tidy, assert_w3c, and assert_xmllint. assert_validates
Validate all requests in your controller, integration, and http tests by adding these lines to your RAILS_ROOT/test/test_helper.rb file:
ApplicationController.validate_all = true ApplicationController.validators = [:tidy]
[edit] Helper tests
http://nubyonrails.topfunky.com/articles/2006/04/07/test-your-helpers
./script/plugin discover ./script/plugin install helper_test
Or, since plugin discover usually breaks for me, just give an explicit URL to install:
./script/plugin install --externals http://topfunky.net/svn/plugins/helper_test/
Add something like this to your test_helper.rb
require "vendor/plugins/helper_test/generators/helper_test/templates/helper_testcase.rb"
./script/generate helper_test Application
[edit] Cache Test [Caching (category)]
The page cache test plugin adds assertions to Test::Units::TestCase to check the caching and expiring of pages, actions and fragments in tests
[edit] Watir on Rails
http://rubyfurnace.com/plugins/watir_on_rails
- Generate, develop, and run Watir tests from Rails. Access to fixtures and ActiveRecord from within your Watir tests. Decorate your Watir browser object with a site-specific language similar to Rails Integration Testing.
- http://rubyfurnace.com/plugins/watir_on_rails
- Repository Path: svn://rubyforge.org/var/svn/watir-on-rails
- Homepage: http://watir-on-rails.rubyforge.org/
[edit] RSpec on Rails
http://blog.nicksieger.com/articles/2006/11/15/rspec-autotest-now-a-rails-plugin
Install RSpec on Rails, following the original instructions. As of RSpec 0.7.3, the specific version of ZenTest is no longer required. Also, diff-lcs is required to show unified diff output on should == failures.
gem install zentest -v 3.4.1 gem install diff-lcs gem install rspec script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_2/vendor/rspec_on_rails/vendor/plugins/rspec
[edit] HpricotForms
| Categories/Tags: | [Testing forms (category)] |
|---|---|
| Homepage: | http://blog.yanime.org/articles/2006/07/19/hpricotforms |
| Source code: | http://choonkeat.svnrepository.com/svn/rails-plugins/hpricot_forms
|
| As listed in other directories: | http://rubyfurnace.com/plugins/hpricot_forms
|
http://blog.yanime.org/articles/2006/07/19/hpricotforms
def test_login_logout page = get :new # first, let's get to the login page form = page.forms.first # next, get a reference to the form form["user[login]"] = "Joe" form["user[password]"] = "topsecret" page = form.submit(self) # after setting the form values, submit it # 'page' now references the page after login assert_response :success # page.links # returns you an array of links found on the page page = page.links("@id='signout'").click(self) assert_response :success end...
The main thing I like about this API is that the field names in the form are real. If you're careless like me, and modify your action templates (.rhtml files) but forgot to change both your controller and test, traditionally your test will still pass because you were passing in parameters explicitly, e.g.
post :login, {:user => {:login => "Joe", :password => "topsecret"}}even though your HTML form fields were changed to "
account[login]" and "account[passwd]". But HpricotForms will raise an exception when you try to set values into fields that does not exist in the rendered form.
[Code generation (category)][3]
To help kick off the writing of code, form.to_code will print out a series of variable assignment statements based on the default values from the form, e.g.
form = page.forms.first puts form.to_code # this prints lines of Ruby code like: # # form["from"] = "sender" # form["email[0]"] = "email1" # form["options[]"] = ["html", "read-receipt"]
[edit] Form Test Helper
| Categories/Tags: | [Testing forms (category)] |
|---|---|
| Homepage: | http://www.jasongarber.com/articles/2006/10/24/easier-testing-of-forms-form_test_helper |
| Source code: | http://form-test-helper.googlecode.com/svn/form_test_helper/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/form_test_helper
|
http://rubyfurnace.com/plugins/form_test_helper
With the form_test_helper Rails plugin, your functional and integration tests can work more like the browser.
No longer do you need to feed params to an action:
post :create, :name => ‘Pickaxe’, :category => 1, :out_of_print => 0 assert_response :success... and then watch it silently break when you change your form but forget to change the test. With form_test_helper, you just call up the form, change the fields you want, and submit it.
get :new submit_form do |form| form.name = ‘Pickaxe’ form.category = "Programming” form.out_of_print.uncheck end assert_response :success...or simply:
submit_form :name => ‘Pickaxe’, :category => ‘Programming’, :out_of_print => falseThis is advantageous because it uses the action and method of the form [that is, you don't have to duplicate it in your tests], verifies that the form and the fields you specify are present and not misspelled, and it preserves any hidden or default values that may be in the form [no need to duplicate them in your tests], like in form_spam_protection.
Features
- Can select_form by dom_id or url—or call without arguments if there’s only one form
- Specifying a field name that doesn’t exist raises an exception
- Fields that are selects (dropdowns) won’t let you set a value that’s not in its options
- Selects can set using the option label or the option value
- Inspect/verify the options for selects and radio buttons
- Works with RESTful links and forms – :method => :put, :delete…
- Checks for the presence of a submit button when you submit the form
- Works in functional and integration tests
- Assert presence of and follow links
Comments:
- Avoids duplication
- Easier to write and read tests
- Makes tests less brittle (prone to break due to minor changes in the System Under Test)
[edit] Contentful
| Description: | This is a Rails plugin to make it easy to write tests that compare the HTML content of rendered views against expected content stored in files, and to create, compare against, and update that expected content.
|
|---|---|
| Readiness: | 2 - Pre-Alpha
|
[edit] Unsorted / to do
http://toolmantim.com/article/2007/3/27/if_youve_been_ignoring_mocking If you've been ignoring mocking
