When I started at Tobi, our test cases would run in less than 5 minutes, ahhh good times, good times! I remember this emphatically since Dileep's tests would take twice as long to run on his Linux box. As more and more tests were introduced, including integration tests with our own DSL (another post) our tests started to take so long that we needed a continuous build solution (again, another post) since we could no longer run them locally. Now, I am aware that there are things we can do and have since done to speed up the tests, mainly writing better tests, however that is not the point here.
Since just the unit tests now take upwards of 45 minutes to run locally on a 2.4 GHz Intel Core 2 Duo it was extremely useless and a bit ridiculous seeing .............EEFFF....E...F.......E........F......FFFEEEEE...... and having to wait until the end of the run to see what actually happened! Tobi Test Timer will show you immediately when an error or failure occurs. It is a bit funny since this is very useful and yet a side affect of the plugin!
The plugin's main functionality is to show you slow tests. That is, tests that take longer than the threshold will be echo'd out as slow.
SLOW TEST: 69.055537 - test_should_search_for_customers_with_multiple_cancelled_orders_by_category(CustomerSearchTest)
SLOW TEST: 12.595146 - test_multiple_even_exchanges(OrderProcessReturnTest)
SLOW TEST: 10.899179 - test_return_and_exchange_with_store_credit_reused(OrderProcessReturnTest)
The spirit of the plugin is to display your slow tests so you can speed them up and then re-adjust the threshold, hence slowly speeding up your build.
The plugin can be found here on Github and you can use git to install it.
Lets talk about the code, it is only 38 lines and basically opens up Test::Unit::TestCase and does a few alias_method_chains. There are a few interesting pieces of code. First the fact that you MUST extend the instance of Test::Unit::TestResult. I was unable to get this to work by extending the actual class or even opening up the class. The other interesting pieces is that you do not want to define the alias method more than once.
- class Test::Unit::TestCase
- def run_with_test_timing(*args, &block)
- result = args[0] # Test::Unit::TestResult
- # extend the class to do what we want
- # Note this does not work with the regular class_eval
- # I was unable to successfully oepn up Test::Unit::TestResult
- result.class.class_eval do
- def add_error_with_log_error(error)
- puts "\nERROR: #{error.test_name}"
- add_error_without_log_error(error)
- end
- def add_failure_with_log_failure(failure)
- puts "\nFAILURE: #{failure.test_name}"
- add_failure_without_log_failure(failure)
- end
- alias_method_chain :add_error, :log_error unless method_defined?(:add_error_without_log_error)
- alias_method_chain :add_failure, :log_failure unless method_defined?(:add_failure_without_log_failure)
- end
- #time the test
- begin_time = Time.now
- run_without_test_timing(*args, &block)
- end_time = Time.now
- duration = (end_time - begin_time)
- long_test = duration > '5.0'.to_f
- puts "\nSLOW TEST: #{duration} - #{self.name}" if long_test
- end
- # Since we require the test helper in all tests we need to check this out.
- alias_method_chain :run, :test_timing unless method_defined?(:run_without_test_timing)
- end
References
http://agilewebdevelopment.com/plugins/tobi_test_timer
http://github.com/gittobi/tobi_test_timer/tree/master
git://github.com/gittobi/tobi_test_timer.git