Close and Go BackBack to Viget

Test Drive mod_rewrite Rules With Test::Unit

Patrick Reagan
Patrick Reagan, Development Director, February 24, 2009 6

I've generated redirects in Apache using mod_rewrite so many times, I can do it in my sleep. Unfortunately, I think I have done them in my sleep. The result? Time wasted chasing down unexpected application behavior and the wrath of the Viget development team.

When a recent re-launch brought with it a change to the Information Architecture of the site, I was determined not to repeat the mistakes of the past. Though this site isn't a Rails application, I wanted to use familiar tools to automate the verification of the rewrite rules I was about to create. In thinking about this a bit more, it was clearly a "behavior-driven" approach - I was specifying the behavior of the web server before its implementation.

Since I'm writing about the majority of my tests with Shoulda these days, I immediately gravitated toward its familiar DSL to come up with this:

should_redirect '/pocket_guides/', :to => '/live-healthy/pocket_guides/'

I had two goals in mind with this approach: clarity and readability. To see how well I did, I shared the collection of tests I created with Kara to ensure that the redirects they represented were accurate. Within minutes, she had corrected a couple of errors I had introduced. My test suite was now complete.

Putting it Together

I wanted something that was easy to use, so I added the should_redirect macro to a subclass of Test::Unit::TestCase which I used in my actual tests:

class ChecRedirectTest < HTTPRedirectTest
  self.domain = 'chec.lab.viget.com'
  should_redirect '/pocket_guides/', :to => '/live-healthy/pocket_guides/'
end

From here, I was able to adopt a familiar behavior-driven approach. I wrote failing tests, and then generated the appropriate configuration within Apache to make the tests pass. When I broke a regression, I was quickly alerted to the fact with a familiar report:

Running Tests

Unintended Consequences

Getting the redirects correct isn't the only place that errors can creep in. To cover all cases, you need to have checks in place to ensure that URLs that are not supposed to redirect are behaving as expected. I added this ability using a similar syntax:

  should_not_redirect '/'

With that guard in place, I was instantly alerted to problems with unexpected redirection:

No Redirection

Trying it Out

The implementation of the HTTPRedirectTest class is available as a Gist on Github. Go ahead and grab it, and start off by creating your own redirection test as a subclass:

require 'http_redirect_test'
  
class ExampleRedirectTest < HTTPRedirectTest
  self.domain = 'development.example.com' # domain to test against
  should_not_redirect '/'                 # initial guard
end

All green? Continue adding tests. If you want to check redirection of subdirectories, you can do that as well:

  should_redirect '/blog/*', :to => '/*'

When you're satisfied with the redirection rules, testing in production is just as easy. Just change the domain to point to your production server:

class ExampleRedirectTest < HTTPRedirectTest
  self.domain = 'production.example.com'  # domain to test against
  should_not_redirect '/'                 # initial guard
  should_redirect '/blog/*', :to => '/*'
end

Bonus: Refactoring Rewrite Rules

Red, Green, ... Refactor? Once you've built up your regressions, you can start tweaking some rewrite rules without worrying about breaking others – your tests will back you up. These rules I created as part of my test plan can be condensed from this:

RewriteRule ^/resources/faq/?$ http://chec.lab.viget.com/live-healthy/faq/ [QSA,R=301,L]
RewriteRule ^/resources/faq/(.+)$ http://chec.lab.viget.com/live-healthy/faq/$1 [QSA,R=301,L]

Down to this:

    RewriteRule ^/resources/faq/?(.*)$ http://chec.lab.viget.com/live-healthy/faq/$1 [QSA,R=301,L]

The behavior stays the same, but our rewrite rules are now easier to understand (as far as mod_rewrite rules go).

Additional Resources

Adam Meehan said on 02/25 at 06:56 AM

Nice work!

I have been wondering about how to approach a Rewrite rule tester. I was pursuing a way to do it using Apache directly, so offline. It should be possible and there is a perl library to run apache tests but its dated and a bit ugly.

Adam

Patrick Reagan said on 02/27 at 03:36 PM

@Adam: I’m glad you found that useful.  A friend pointed me to a related approach of using external tools to test non-application code.  The result is a way to use Cucumber to generate system tests that plug directly into Nagios for monitoring your app.  It’s worth a look just to get an idea of other areas where this type of approach would fit.

Matt said on 04/08 at 08:34 AM

Hi Patrick. Thanks very much for this post, it’s proved very helpful to my work at Reevoo, so I have packaged it up as a gem. it’s on github at http://github.com/shadowaspect/http_redirect_test

thanks
Matt

Patrick Reagan said on 04/09 at 12:43 PM

@Matt: Cool - I have some other ideas floating around for it, so I’ll have to contribute.  Thanks!

Dean said on 07/02 at 12:39 PM

I must have missed what about the ruby class (or your environment) makes sure that Apache is in the loop and processing rewrite rules.

My scenario is that we use litespeed (with a mod_rewrite), but my impression was that controller testing didn’t require (or use) a running web server.

Thanks for any help..

Patrick Reagan said on 07/02 at 02:27 PM

@Dean: This isn’t an approach to do controller testing.  It’s simply providing a DSL to *externally* test your web server by treating it as a black box and examining the responses returned.  In my case, I was using Apache, but there’s no reason that this approach wouldn’t work against Litespeed, lighttpd, Ngnix, or others.

Check out Matt’s gem for a quicker way to get started using this.

Name:

Email:

URL:

Not an infant or robot? Please prove it:
What color is the sky?

Some HTML (strong, a, em) is allowed.

Notify me of follow-up comments?

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


How many minutes in an hour?

Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.