Close and Go BackBack to Viget

First Hack Night a Success!

Patrick Reagan
Patrick Reagan, Development Director, June 19, 2008 4

We had about 10 people show up to Murky last night for a few hours of coding and conversation. Some ended up working on personal projects, others paired up to learn more about both the latest version of Passenger and JQuery. We finished up just in time to grab a few post-hack beers at Liberty Tavern across the street (not the coolest place, but it was close!).

We plan on doing this again soon. There's been talk of potentially moving it to Busboys & Poets in Shirlington, but Matt will post details about the next night as they're available.

Thanks to everyone who came out for making it a fun night!

Testing Helpers In Rails 2.1

Justin Marney
Justin Marney, Web Developer, June 17, 2008 2

The conventional approach to testing helpers in Rails has, until recently, always been relatively subjective. I have come across different strategies including generators, plugins and hand-hacked setups depending on where I looked. In sharp contrast, the controller testing convention has been DRY'd up and moved into the framework in the form of the ActionController::TestCase class. Fortunately, Brian has been working on some helper testing tools and recently stumbled across the ActionView::TestCase class. If you have been looking for a convenient, framework supported way to test your helpers, look no further. This class provides the same conventions as ActionController::TestCase and makes testing helpers a breeze.

Test Your Helpers Using ActionView::TestCase
Here is an example that shows how to use ActionView::TestCase along with Brian's assert_tag_in. First, create a test named HelperNameTest and inherit from ActionView::TestCase. Then just call your helper methods from your tests, it's that easy.

module ApplicationHelper
  def special_header()
    content_tag :div do
      link_to('Edit Profile', edit_account_path)
    end
  end
end

class ApplicationHelperTest < ActionView::TestCase
  def test_special_header_should_render_edit_link
    tag = special_header(:edit_profile => true)
    assert_tag_in(tag, :a, :attributes => {:href => edit_account_path})
  end
end

Worth noting here is the ability to test methods that rely on url_for without needing to create a temporary controller in your own testing code. I also want to point out that Chu Yeow did cover using ActionView::TestCase in his Living on the Edge series, but it totally slipped past me :)

Update

ActionView::TestCase is not loaded automatically. Be sure to require 'action_view/test_case' in your test_helper.

Inheritance is Unlikely

Justin Marney
Justin Marney, Web Developer, June 05, 2008 8 What do you do when you identify common sets of shared behavior across several classes?

A common solution is to move the shared code into a base class and inherit.
# Filled with api interaction behavior
class Base
  def api_specific_behavior
    puts "connect to api"
  end
end

class User < Base
  def user_specific_behavior
    puts "I am a user"
  end
end
The problem with inheritance in this situation lies primarily in a subtle violation of the Liskov Substitution Principal which essentially states that inheritance should implement an "is-a" relationship. If we have a class called User that inherits from Base, are we trying to model the part of our domain where a User is a Base? This doesn't make sense given that we never actually have any Bases (i.e. instances of Base).

Continue reading "Inheritance is Unlikely"

Testing for HTML Tags in Rails Plugins

Brian Landau
Brian Landau, Web Developer, June 04, 2008 5

When creating Rails plugins that add ActionView helpers, we often test to ensure they produce specific tags with specific attributes. When testing this type of assertion within controller tests, we have the very useful assert_tag and assert_select; but in plugin tests and elsewhere, these aren't available. Adding this functionality to your own tests turns out to be somewhat convoluted.

The first realization you come to is that assert_tag and assert_select can't be used because they test against the response body of a controller. Since we're trying to test functionality independent of other components, using ActionController when it's not necessary is not recommended. You might be tempted at this point to just test against a regular expression and forget using these methods. That is doable, but a little unmanageable in the long run. First off, attributes can't be expected to be in a specific order, and their order doesn't matter, so you don't want to test for that. This will even initially make your regexes fairly complicated. Here's an example of testing for a specific name attribute:


def test_for_name_attribute
   tag = text_field_tag('login')
   assert_match /<input\s+(([\w^(name)]+="([^"'><]+)?"\s+)+)?name="login"((\s+[\w^(name)]+="([^"'><]+)?")+)?(\s+)?\/>/, tag
end

As you can see, even matching one attribute can result in a extremely long and ugly regex. What if we want to match multiple attributes and nested tags? This quickly becomes untenable.

If you look to include the component used by assert_tag to find and match specific tags, you will find require 'html/document'. Try just requiring this by doing this at the top of your test:


require 'test/unit'
require 'rubygems'
require 'active_support'
require 'action_view'
require 'html/document'

You'll find you get a no such file to load -- html/document (MissingSourceFile) error. If you dig around in the Rails code, you'll find it lives in action_controller/vendor/html-scanner. To be able to include html/document, you need to first include action_controller.

Now that we have html/document included, let's make a test helper method to match a tag specification. This takes little change from the original assert_tag method.

def assert_tag_in(*opts)
   target = HTML::Document.new(opts.shift, false, false)
   opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
   assert !target.find(opts).nil?, 
     "expected tag, but no tag found matching #{opts.inspect} in:\n#{target.inspect}"
end

With this, you can now use assert_tag_in to do the same test as above:

def test_for_name_attribute
   tag = text_field_tag('login')
   assert_tag_in tag, :input, 
     :attributes => {:type => 'text', :name => 'login'}
end

The whole code for a sample test_helper.rb file for plugins can be found in this pastie.
Also, look at the documentation for assert_tag for all the options available.

From this, an assert_no_tag_in could easily be made. You could use a similar technique to make a assert_select_in, although assert_select is much more complex and often overkill for simple helpers where you aren't testing as large a number of tags or as complicated as nesting.

Update:

Given the interest in a hpricot version of the assert_tag_in, I've put together one:

def assert_tag_in(target, match)
   target = Hpricot(target)
   assert !target.search(match).empty?, 
   "expected tag, but no tag found matching #{match.inspect} in:\n#{target.inspect}"
end

With this you can then write a similar test like this:

def test_for_name_attribute
   tag = text_field_tag('login')
   assert_tag_in tag, 'input[@name="login"]'
end

The alternative test_helper.rb using hpricot can be found in this pastie.

Make Apple’s Address Book Pull from Exchange

Tony Pitale
Tony Pitale, Web Developer, May 28, 2008 1 If you've ever wished you knew who you worked with but were to afraid to ask...entourage.

Continue reading "Make Apple’s Address Book Pull from Exchange"

A Development Community for Viget Labs and Beyond

Every team member here at Viget Labs strives to be an innovator. We members of the development team are no different - that's why we're constantly engaging in community discussions and exploring the unknown that is the next generation of open-source web applications.

Viget Is Hiring!

Viget has job openings for Ruby Developers, Interns, and Front-End Developers. Learn More »

Upcoming Events

O’Reilly’s Open Source Convention - July 21 - 25
Clinton R. Nixon, our other Senior Developer, will be speaking on "Extending Rails: Understanding and Building Plugins."

Recent Comments

Smashing! Thanks for outlining what’s needed to be done so precisely :-)