Darwinweb

Namespacing Tests with ZenTest's Autotest

April 21, 2007     

Update June 13, 2007When I wrote this article I mistakenly assumed that ZenTest followed Rails namespacing conventions for functional testing. However after creating a namespaced controller (as opposed to just a namespaced test), I discovered that ZenTest is actually wrong. Other people have noticed as well, there is a patch over at RubyForge.

Namespacing is treated like an ugly step child by the Rails community. I think it’s pretty good advice to avoid namespacing in Rails if you can, especially if you are just getting started with Rails. But in general I chafe against the “don’t do it because we said so” attitude. Do they know why I need namespaces? There are legitimate needs for namespaces, and they shouldn’t necessitate a move up to some breed of “enterprise stack”. Here’s one example of how namespacing fits a unique need.

Unlimited Customizable Domains

I’m currently working on is a subscription app that provides a complete website solution for clients. There is one admin domain, but each subscriber has one or more public-facing domains, sort of like Shopify. The difference is that these sites need to be much more customizable than something like Liquid Templates allows. The solution was to do a little hackery with Matt McCray’s Theme Support plugin. This allows each site to have its own unique content pages and override any templates as necessary. There are many little pieces working together here, but overall it has been pretty easy to develop.

Namespacing Tests

The ability to override any template based on domain allows me to do almost anything a client asks without the expense of making a general purpose feature for everyone, but these “theme” templates are a new point of failure. I realized that without the ability to test them, there was a big danger of unnoticed 500 errors.

But how to test them? The theme is chosen based on the domain, so they can be tested by adding the following to a functional test’s setup method:

@request.host = 'mydomain.local'

Simple enough, but the other problem is what to name the test class and file. I already have a generic test class for each controller, but I potentially need one for each domain that overrides one of the controller’s templates. To avoid namespacing, I could add a domain prefix to each additional test class. The problem is that my functional tests directory could grow to hundreds or thousands of files, making it hard to locate the main functional tests.

A far more elegant solution is to create a directory for each extra domain and add any functional tests under a domain namespace. To accomplish this I put a module declaration in environment.rb so I can use :: shorthand for declaring each test class:

module MydomainTest; end

Then each functional test is defined in its respective file as

require File.dirname(__FILE__) + '/../../test_helper'
require 'generic_controller'

# Re-raise errors caught by the controller.
class GenericController; def rescue_action(e) raise e end; end

class MydomainTest::GenericControllerTest < Test::Unit::TestCase
  def setup
    @controller = GenericController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    @request.host = 'mydomain.local'
  end
end

This is just a duplicate of the original GenericControllerTest with 3 changes:

  1. Add “/..” to the test_helper include since it is one directory deeper.
  2. Add “MydomainTest::” namespace to the class definition.
  3. Add “@request.host = ‘mydomain.local’” to the setup method.

Originally I had set this up with “Mydomain” as the module name, and it worked fine as far as rake test was concerned. However ZenTest’s autotest could not map the class back to its file. However, with the module named “MydomainTest” autotest worked just as expected.

It’s worth noting that the namespaced test won’t be run automatically if the controller is changed, however I plan on fixing this with some trivial changes to autotest_rails.rb.