Difference between rspec style spec names and test unit? Three lines.
A discussion I overheard in irc made me wonder what possibilities would be opened up if instead of naming tests by convention (test_)all publicly accessible methods were run as tests. This lead down a path to this thought:
class RspecInTU
class << self
alias it define_method
end
it "should say hello" do
puts "hello"
end
end
RspecInTU.new.send "should say hello"
Technomancy told me that it’s easy to actually get miniunit to use all public methods as test methods and came up with a minimum working base
Of course, I wondered if Shoulda already did something like that, and some people confirmed that it does. It’s still neat.
A revamped plugin/gem for benchmarking your ruby/rails test::units 6
and what I learned about hacking on Test::Unit.
For quite a while I’ve been using Geoffrey Grosenbach’s test_benchmark to see what tests were most egregiously slowing down whole test suites. I (and he as well, actually) was quite dissatisfied with its approach of spamming the console with the full output after each file completed running, as it made the plugin unusable for just keeping enabled. Unfortunately, how to hack test/unit best wasn’t immediately apparent the last time I looked into.
This time I’ve figured it out: I’ve now reworked the plugin so that it waits until all the tests are done running, and then outputs the slowest 10, while dumping the full list to the log (if you are in Rails, or Loggable is otherwise defined). Other info and options can be found at the new github home of test_benchmark.
The original version just redefined Test::Unit::TestSuite#run to wrap it with some benchmarking output.
#code trimmed down to functional base
class Test::Unit::TestSuite
def run(result, &progress_block)
@tests.each do |test|
test.run(result, &progress_block)
#code to store benchmark times here
end
#code to output benchmark times here
end
end
The problem with this is the slightly confusing definition of TestSuite within test/unit (or at least how it ends up working the reality of most projects testing setup). I (and perhaps Geoffrey too) assumed the usual project wide definition of a ‘test suite’ being the entire collection of tests. As I was putting output statements through the codebase, I noticed that each individual test file was being treated as a separate TestSuite, despite inheriting from TestCase. So that meant each time a file completed, the benchmarking code at the end of TestSuite#run spammed the console.
Perhaps there is a way to better organize your tests into Suites, so that this doesn’t happen, but that is moot, because this is how pretty much all projects are organized in reality. As such, I needed to rework the test_benchmark codebase to handle this better.
Instead I used Test::Unit::UI::Console::TestRunner (which is instantiated when you runs test from the console, shockingly enough), which already uses the hooks for individual test, as well as entire rest run, start and stop. I just added a bit more functionality to these functions and BAM, easy-peasy benchmarking that only outputs when the full test run is done.
alias started_old started
def started(result)
started_old(result)
@benchmark_times = {}
endalias finished_old finished
def finished(elapsed_time)
finished_old(elapsed_time)
benchmarks = @benchmark_times.sort{|a, b| b1 <=> a1}
output_benchmarks(benchmarks)
endalias test_started_old test_started
def test_started(name)
test_started_old(name)
@benchmark_times[name] = Time.now
endalias test_finished_old test_finished
def test_finished(name)
test_finished_old(name)
@benchmark_times[name] = Time.now – @benchmark_times[name]
end
Rails 2 foxy fixtures and named_scope/has_finder closure issues
If you are using a has_finder (or possibly named_scope, I haven’t confirmed this myself), remember that unless you wrap your condition => in a lambda it is going to be evaluated early – or at least earlier than fixture creation, it seems. At work we had a case where one model had a finder that depended on its belong_to being in a subset of another, basically:
has_finder :all_active, { :conditions => {:status_id => Status.active_ids} }
This work in production, where the ids don’t change, but since we are using the newer fixture approach on this model the ids are created dynamically. Given how fixtures load, the table will probably be populated with the old values, at parse time for that class, then the test will run, and the fixtures will reload, and they will not match anymore.
This is easily fixed, when you realize what is happening:
has_finder :all_active, lambda {|| { :conditions => {:status_id => OtherModel.active_ids} }}
Running Internet Explorer on Mac through Wine
While Parallels is awesome and all, for web developers, I got tired of having to run a whole VM at all times for testing. I decided to see if I could get IE running through WIne, and just use my Parallels install for a final browser test. I basically followed these directions. with a couple additions:
- X11 wasn’t running my .bash_login, so I moved the entire file to .bashrc, and then made a new one that sourced (“source ~/.bashrc”) so that both the OS X terminal and X1 got all my bash set-up stuff.
- I added “export DISPLAY=localhost:0.0” to my new .bashrc
- I copied the contents of my Basecamp/Parallels install font folder to my “.wine/drive_c/windows/fonts/”
So now I can just run IE through X11 for my day to day testing. I didn’t get IE7 running, because the betas of that had some issues on OS X, but when they have it polished I’ll give it another shot.
autotest reminding you to test one thing at a time 2
I was fixing a bug via TDD (Test Driven Development) and having a bit of trouble. I couldn’t quite get my test to test for the conditions that prevented the bug. As I went upstairs to grab a snack, I realized that my problems was I was conflating two tests together.
I use autotest a lot, but sometimes when I’m doing something I shut it off, because I want to manually run one particular set of tests at need. With a system not quite speedy enough, that sometimes makes sense.
But, as part of my Eureka moment I realized that the reason I had turned off autotest, in this case, was that I wasn’t taking small enough baby-steps. I was breaking too many things at once. If I had tested for each individual thing, and developed by each small test, like I realized to do later, I could have kept autotest on without it blowing up at me.
Dynamic Scaffold Resource test helper now sucks less
I added a couple instance variables that can optionally be set so that the test helper can actually be used in something beyond the skeleton of the plugin’s own test environment. You can use it in an authenticated environment now, because you can set @scaffold_session_params and you can use it on resources that have validation in their models, because it will use @scaffold_dummy_object for the create and edit test, if you set it.
Didn’t change the core of the plugin itself, but the testing piece really was useless, so I had to add this just to use it on the project I made the plugin for in the first place.
Debug failing test with assert_dom_equal
If you are using assert_select, which you should be, you might get into situations where you are unsure why it is failing, and its output isn’t always illuminating for that. You can do a quick sanity check by doing a
assert_dom_equal(‘foo’, @response.body)
right before the failing tests and you’ll get the full response body for the test dumped to your console.
It’s useful for discovering things like the fact that you forgot the = in <%= yield %> in your test layout.
"Extending" classes: redefining an instance method from a module via alias_method
The Typo plugin is finished, but it’s gotten late and I should review it before deploying and releasing it into the wild. A quick post on more I learned, though….
I had to do a fair bit of research to get the second part of the plugin working – the part where I overload/redefine a controller method from my plugin, specifically the permalink method of an ArticlesController. You can spend a lot of time reading about the intricacies of how mixins work in ruby, without getting working code. Dig through ruby-talk and you’ll learn about the difference between include/extend, what happens to “self” in different cases, the reasons for the odd module nesting and, ultimately why the ClassMethods convention caught on.
It’s not so straight-forward to overload a method, apparently, in ruby. God knows I tried each different nesting include/extend combo 6 times, with no luck. Then I came across an easier way for my case, alias_method. While that usage wasn’t exactly what I wanted, the only problem was that it did too much. I just had to drop off the unneccessary bits, and walla:
module TypoPermalinkWithId
....
module ModifyArticlesController
def self.included(base)
base.class_eval do
alias_method :permalink, :modified_permalink
end
end
def modified_permalink
article = this_blog.published_articles.find_by_permalink(*params.values_at(:year, :month, :day, :title))
redirect_to article.permalink_url and return if article.nil? and article = Article.find_by_id(params[:title].to_i)
redirect_to article.permalink_url and return if article.nil? and article = Article.find_by_title(params[:title])
display_article(article)
end
end
end
ArticlesController.send :include, TypoPermalinkWithId::ModifyArticlesController
Testing plugins : Remember to require 'action_controller/test_process'
Before I could even get started on actually coding the second piece of the Typo plugin, I spent a lot of time wondering why I was getting the following error when trying to run my tests:
NameError: uninitialized constant ActionController::TestRequest
I kept looking at the the other plugin I’ve been working on which the tests run just fine on and scratching my head. It turns out in the working plugin I had stashed the “require ‘action_controller/test_process’” line in the testing controller I made, so I didn’t notice the difference. I’ll be moving that back for clarity tomorrow, but remember kids, if you are doing controller driven tests outside of Rails (like in a plugin) put in that line.
ActiveRecord Observers are a very handy thing for plugins
While developing the first piece of the typo-permalink-with-id plugin I spent a lot of time trying to get the module extend/include and module_eval stuff just right so that my code had everything it needed to not through an error when trying to add a after_create to the app code itself. After a bunch if google around I found exactly what I need in teh v2 Agile book. It wasn’t something plug-in specific, but it is a godsend to plugin writers: ActiveRecord:Observer.
Instead of trying to get your after/before code properly mixed into the app code, you can have it nice and cleanly attached via an Observer. All of a sudden my code became very simple.
module TypoPermalinkWithId
class ArticleObserver < ActiveRecord::Observer
observe Article
def after_create(article)
article.permalink = “#{article.id}-#{article.permalink}”
article.save
end
end
ArticleObserver.instance
end