Update: Added code explanation.
I found that the best place to put W3C validation tests would be among your controller tests. In your functionals you already walk across all your pages and deal with authentication, so why not put some more load on that donkey?
First thing – install w3c_validators gem.
sudo gem install w3c_validators
Now, throw the following gist into
test/shoulda_macros/should_pass_w3c_validation_macros.rb
Usage
Now you have a nice w3c validator macros which can be used simply, as part of your controller testing with shoulda.
setup do
get :index
end
should_respond_with :success
should_assign_to :assets
should_render_with_layout
should_pass_w3c_validation # <= Hehe, there you are!
end
What’s happening in the code?
The main part of our macros code is validator itself – we instantiate it by calling MarkupValidator.new. It does all the validation work, but it takes a filename on input. Since we’re actually in controller tests – we usually pull pages using methods like get "/path/to/page". I need to somehow get the full html of that page to give it to the validator, and I need it in a file, since validator only accepts filenames. One way is to create a Tempfile, and throw the @response.body in there. @response object exists in all controller tests, and contains response data which was returned upon calling get "/path/to/page". This is exactly what we need. @response.body holds all of html/js/css which server sends to your browser when you access a certain page in your controller test. All we need to do now is dump the response output into a tempfile so we can feed it to validator.
Looking into the code you can see that I write @response.body to tempfile and pass the path to that tempfile into the validator. What happens next? We get a nicely formatted array of warnings and errors, courtesy of the validator itself. It sends our file to W3C public service and returns with a bunch of errors/warnings. These messages are a perfect candidate for being displayed on test failure (the so-called ‘fail_text’) – since the mere fact that we got any of those messages means we failed. Thus by checking the sizes of both the warnings and the errors array I can tell if my test passed or not – they both have to be zero. If the test fails I display all of them. So what is that substitution that’s happening? Why do I have to go over each warning/error and remove some piece of text? That’s because message contains path to the file – and in our case the path is always to some arbitrary tempfile. Nobody needs that – it only pollutes the test output. So the latter part of the code removes those paths from messages. That’s all.
The crappy part
Obviously, your tests will get very slow, as now they rely on some public service provider. You should definitely implement a switch for turning this type of tests on/off in different circumstances (like when you’re on a Boeing 737 making your way from Antigua to Newark).