Rails / Testing

From WhyNotWiki

Jump to: navigation, search

For general Ruby testing, check out Ruby / Testing


Testing in Rails  edit   (Category  edit)


Rails / Testing  edit   (Category  edit)


Contents

[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) => array

Returns 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.log file. 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 session method, 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 a model.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 reload and 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). Retrieved on 2007-05-11 11:18.


...

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). Retrieved on 2007-05-11 11:18.


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] star_full.gif star_full.gif star_empty.gif 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:

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.

[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 => false

This 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

Facts about Rails / TestingRDF feed
Description [Oops! No type defined for attribute]
Personal tools