Annotate Models Modification

March 29th, 2007

annotate_models has been really useful for me. However when using fixture_groups fixtures in subfolders are ignored. I modified the plugin slightly to include them. With luck these changes can be merged with Dave Thomas ’s version.

Until then, here it is: annotate_models


  ./script/plugin install \
    http://source.elevatorfight.com/public/annotate_models

Fixture Groups

March 29th, 2007

One of the things I like about working with Rails is test fixtures. However when the data model becomes more complex, the fixtures can become rather large. Worse yet, tests that require fixtures among two or more models feel clunky.

I wrote a plugin to help organize fixtures called fixture_groups.


  ./script/plugin install \
  http://source.elevatorfight.com/public/fixture_groups

Example:


  class BlahTest
    fixture_group :group1
    fixtures :foo, :bar

    ...
  end

This will look in RAILS_ROOT/test/fixtures/group1 for the fixtures. If a fixture_group isn’t specified, then the fixtures will use the normal folder.

Update 8/10/07

For the most part, I’ve given up on fixtures in my tests. Although I have it in my mind to check out fixture scenarios soon.

Duplicate Test Names

March 24th, 2007

One of my biggest pet peeves of writing tests in ruby is based on something that makes ruby great. The openness of code.


  ...

  def test_something
    # some assertions
  end

  ...

  def test_something
    # test with different assertions
  end

  ...

Perhaps it’s my consistency towards test names or perhaps I’m focusing on the tree in the forest. But I’ve found myself in this problem more than a few times, editing the first test rather than the second, but the second essentially overrides the definition of the first.

In order to help detect this, I created a small rake task that searches through all tests, and checks the uniqueness of the test names within a test case.

It does ignore commented methods, however right now only accounts for # .

I may convert this into a plugin later, but until then save this in RAILS_ROOT/lib/tasks


> rake test:check_names
Everything looks good

> rake test:check_names
rake aborted 
Multiple methods ["def test_something"] in test/unit/some_test.rb

(See full trace by running task with --trace)

While this keeps an eye out for me, I’m still lazy enough not to run it all the time. I decided to make it a core task for continuous integration, and then check it manually when I’m stumped why tests are not behaving as they should.

Active Record Test Helper

March 19th, 2007

When writing tests for Active Record Models I noticed a lot of textual clutter. I pulled out a small utility to help the assertions on validity: ArTestHelper (an ugly name, I know.)

Install as a Rails plugin:

  ./script/plugin install \
     http://source.elevatorfight.com/public/ar_test_helper

When used in conjunction with Depth Merge. You can zip through validity testing.


  ...
  include ElevatorUp::ArTestHelper
  ...

  def test_missing_important_attributes
    model = create_model_(:name => nil)
    save_and_check_errors(model, :check_errors_on => :name)

    model = create_model(:size => nil)
    save_and_check_errors(model, :check_errors_on => :size)

    model = create_model(:email => nil)
    save_and_check_errors(model, :check_errors_on => :email)
  end

  def test_ugly_emails
    model = create_model(:email => "")
    save_and_check_errors(model, :check_errors_on => :email)

    model = create_model(:email => "aaa")
    save_and_check_errors(model, :check_errors_on => :email)

    model = create_model(:email => "foo bar")
    save_and_check_errors(model, :check_errors_on => :email)

    model = create_model(:email => "foo@bar")
    save_and_check_errors(model, :check_errors_on => :email)
  end

Depth Merge

March 13th, 2007

Merge is rather useful, but it has the limitation of staying shallow. Here is a simple extension of an implemented depth merge, with one caveat. Passing in true to delete_nils will remove the key if the value is nil.

Install as a Rails plugin:

  ./script/plugin install http://source.elevatorfight.com/public/merge_extensions

To gut out the meat just grab this file

An example of use:

  # Without trimming nils
  hash1 = { :a => "foo", :b => { :c => "bar"} }
  hash2 = { :b => { :c => "blah"} }
  hash1.depth_merge(hash2) #=> { :a => "foo", :b => { :c => "blah"} }

  # With trimming nils
  hash1 = { :a => "foo", :b => { :c => "bar"} }
  hash2 = { :b => nil }
  hash1.depth_merge(hash2, true) #=> { :a => "foo" }
Again a very simple utility, however it comes in handy with testing.

  # In Model
  class Foo < ActiveRecord::Base
    validates_presence_of :col1, :col2
  end

  # In Unit Test
  ...
  def setup
    @default_attributes = { :col1 => "foo", :col2 => "bar" }
  end

  def test_missing_important_fields
    # A model with not only col1 == nil, but nil is never passed to the setter
    model = create_model(:col1 => nil)
    # do some checking on validity

    model = create_model(:col2 => nil)
    # again checking on validity
  end

  def test_something_else
    model = create_model
  end

  private
    def create_model(opts = {}) 
      SomeModel.new(@default_attributes.depth_merge(opts, true))
    end
  ...

Having a the depth part of the merge many not be handy in that scenario, but in functional tests they can help with composing the get/post

  # In Functional Test
  ...
  def setup
    ...
    @default_params = { 
      :id => 5, 
      :foo => {
        :col1 => "foo",
        :col2 => "bar" 
      }
    }
  end

  def test_something
    post :some_action, params(:foo => { :col1 => nil })
    ...
  end

  def test_something_else
    post :some_action, params
    ...
  end

  private
    def params(opts = {})
      @default_params.depth_merge(opts, true)
    end
  ...

The premise for this is so tests become more concise, leaving only relevant information. Which in turn make the tests more readable.

Subdomain Assertions

March 8th, 2007

Working with subdomains with rails can be easy, especially with url_for_domain. However testing can be a pain.

assert_redirected_to helped when checking controller / action / parameters, but not asserting the subdomain. I rolled up a small assertion helper that can sit in test_helper.rb.


  def assert_redirect_url(options = {})
    opts = {:only_path => false, :controller => @controller.controller_name}.merge(options)
    assert_equal url_for(opts), @response.redirect_url, "Error matching redirect url" 
  end

Example:


  assert_redirect_url(:subdomain => "test-subdomain", 
    :controller => "test-controller", :action => "test-action")

Update 3/21/07

I recently found that assert_redirect_url collides with an existing rails test helper that is deprecated in Rails 1.2.2. By putting this function definition at the bottom of test_helper.rb it will be overridden. Of course you can always use an arbitrary name and avoid any conflicts.