Sunday, November 30, 2008

Finding which rspec tests produce unexpected outputs

You know running hundred of tests should only show '.', 'F', or 'E' and nothing more. You feel disturbed when some passing tests print debugging statements or rspec matcher complains that one of erb/rhtml views do not have a closing html tag and print a bunch of html outputs.

You want to find which tests among hundreds produce these but you have no clue where to find them. You don't want to search for 'puts', 'p' or 'inspect' from the entire codebase. Nor would you want to find which erb/rhtml files have missing closing tags. What should you do?

My colleague, Toby, suggested me to look at rspec source code, specifically at the formatters so that we can find a spot that runs examples and shows the '.' output. So I did and found the progress_bar_formatter.rb in lib/spec/runner/formatter.

def example_passed(example)
@output.print green('.')
@output.flush
end


We can put a print statement to identify which example (rspec test) is passing. Either @output.print example.description or @output.print example.inspect should be fine. Once we know the example names, we should be able to find where they are. Now we can fix the problem and make all test outputs produce those little green dots.

Sunday, November 23, 2008

Ruby: Treat object and collection uniformly

Problem: You write a method that takes a collection and iterates over it. However, you also want to accept a single object parameter as well.

Solutions: Convert a single object into an array so that we can manipulate the parameter uniformly. As always, there are many ways to do this in Ruby. However, I prefer the last one.

1. Make sure it is an array by using Object#to_a. However, to_a is deprecated and not recommended.

def f(a)
a = a.to_a
a.each { |item| ... }
...
end


2. Check the parameter type and wrap it into an array if it is not an array already.

def f(a)
a = [a] if a.is_a? Array
...
end


3. Duck-type check whether it responds to :each method

def f(a)
a = [a] unless a.respond_to? :each
...
end


4. Use splat operator. This also does the job. It is concise but less readable.

def f(a)
a = [*a]
...
end


5. Use Array#flatten. I prefer this one. It is readable.

def f(a)
a = [a].flatten
...
end


Is there any other more way?