Minimalist testing for Ruby applications
Last four months I have been developing web and backend applications for fun and profit without Rails - using the light weight Sinatra framework. Besides technical solutions there is also an important philosophical attitude I have learned in that Sinatra community.
In opposite to Rails they completely decline the cargo cult (also mentioned by Koz in a slightly different context) where one writes a line or two, he has seen in the README of the plugin and expects something magical to happen. And often it does not happen or happens in an unexpected way.
By contrast, in Sinatra community, if one develops a chunk of functionality, he thinks can be helpful for others, instead of creating a plugin or gem one publishes a gist at github so others can copy, paste and use it as inspiration for implementing the 5 percent of functionality they really need.
I found out that for me the RSpec approach does not work. The tests should give me more confidence, that everything works, especially for refactoring. Through all the magic and extensive metaprogramming RSpec creates more uncertainty for me instead.
Test::Unit is on the other side a bit “underkill” but it is easy to extended it with features I am missing:
- use full sentences to name the tests
- declare tests as pending or non-critical
- enable the same level of abstraction in the test implementation and provide an overview of a test
If folded in the editor, a test looks like following, providing an overview of what is going on in the test:
-class SignupTest < FunctionalTestCase
- test 'registration workflow' do
+ prepare 'a customer with wrong bank details' do
+ expect 'errors only after the offline check' do
+ expect 'update should clean errors and bank details check identifier' do
end
end
This single test steps can be also printed out during the test execution if you set the environment variable VERBOSE_TEST=true.
For comparision here is the full source code of the integration test:
class SignupTest < FunctionalTestCase
test 'registration workflow' do
prepare 'a customer with wrong bank details' do
put '/customers/inva', create_customer_xml(
'account' => '123456',
'bank-number' => '36010043'
).to_xml, {'HTTP_X_ON_BEHALF_OF' => 'functional test'}
assert_equal 202, last_response.status
end
expect 'errors only after the offline check' do
assert_equal 0, Customer.find_by_customer_id('inva').customer_errors.size
Customer.check_and_promote_all
a = Customer.find_by_customer_id('inva')
assert_equal 1, a.customer_errors.size
assert_match /invalid/, a.customer_errors.first.message
end
expect 'update should clean errors and bank details check identifier' do
get '/customer/inva', {}, {'HTTP_X_ON_BEHALF_OF' => 'functional test'}
new_doc = response_doc()
new_doc.select_single_node('//bank-name').set_text('Other Bank')
put '/customer/inva',
new_doc.to_xml,
{'HTTP_X_ON_BEHALF_OF' => 'functional test'}
a = customer.find_by_customer_id('inva')
assert_equal 202, last_response.status
assert_equal 0, a.customer_errors.size
assert a.schufa_request_identifier.blank?, 'request identifier is not cleaned.'
end
end
end
So my testing infrastructure mainly consists of
- the excellent Rack::Test facility, is also standard for new Rails versions
- my Test::Unit extension, grab it on github
- my project-specific test_helper, which can programatically create entities filled with test data and contains project-specific assertion helpers
Comments
blog comments powered by Disqus