Posts Tagged ‘debugging’

Hair tearing

Monday, November 17th, 2008

I’ve been struggling with a bug all day, and I’m stuck, so it’s time to blog.

There’s a wonderful set of matchers in Rspec on Rails that lets you do something like this:

describe "some html thing" do
  code = '<div><a href="wherever">link</a>, <a href="somewhere">another</a></div>'
  code.should have_tag('div') do
    with_tag('a', 'link')
    with_tag('a', 'another')
  end
end

Basically, since HTML is nested, it’s really nice to be able to nest your matchers.

Unfortunately, has_tag seems to be the only matcher that works like this. I am writing some code for Forkolator that generates god files, and I find myself wanting to do something like this:

it "should have a proper mongrel startup command" do
  code.should match /"mongrel_rails start[^"]*"/ do
    with_match /-c #\{RAILS_ROOT\}/
    with_match /-p #\{PORT\}/
    with_match /-d/
  end
end

Alas, the match method cannot do this. In the interest of writing clean specs and in learning as much as possible about advanced Ruby features, I decided to write it myself.

Unfortunately, looking at the code for has_tag didn’t help much. It’s written as a wrapper of the AssertSelect class, and it seemed like yak shaving to dive in to that.

So I started from scratch. You can my code and my spec so far.

Here’s where it gets weird. The examples all pass. Or they appear to, anyway. I get all nice green dots, 0 failures. If you look closely though, you’ll see that only 5 examples are running, even though I count 8 shoulds in my spec.

Weird, huh? Turns out, the matchers that actually use the additional block (the whole point of this crazy project) don’t ever return to their examples. Rspec seems to just move on along to the next one. Now why would that be?

I broke out my ruby debugger, and started stepping through the tests to see where the problem might be, and lo and behold right when MatchNGo::matches? is about to return on line 37, The debugger throws up an error:

/var/forkolator/apps/forkolator/vendor/plugins/rspec/bin/spec:4Tracing(1):/var/forkolator/apps/forkolator/vendor/plugins/rspec/lib/spec/expectations/extensions/object.rb:31 ExpectationMatcherHandler.handle_matcher(self, matcher, &block)
INTERNAL ERROR!!! (eval):1:in `[]‘: can’t convert String into Integer
/usr/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.2/lib/ruby-debug-base.rb:31:in `eval’

I’m really not sure what to make of this. Is it a bug in the debugger? Could it be the reason my code is all screwy? Is it a bug in rspec? What’s going on?

Update: I’ve given up for now and am just writing my tests like this:

1
2
3
4
5
6
7
8
it "should have a nice startup command" do
  m = /w.start = "([^"]*)"/.match(@god)
  cmd = m[0]
  cmd.should match(/mongrel_rails start/)
  cmd.should match(/-c \#\{RAILS_ROOT\}/)
  cmd.should match(/-p \#\{port\}/)
  cmd.should match(/-d/)
end

It’s not pretty, but it works. Maybe as I get better at ruby I’ll be able to go back and get it working the right way.

Debugging Rails Tests

Thursday, November 16th, 2006

I am just getting started with Ruby on Rails, an application framework that everyone seems to love for web applications. I am learning lots, so I figured I’d post things I learn here.

I’m doing “Test Driven Development” on this project, so instead of writing code first, I should write a test, right? So, I open test/unit/myclass_test.rb, add a test, save the file, and then run

rake test_units

which runs the tests. Of course, my code doesn’t work and I get an error, so now I want to debug my test. I want to see what went wrong. I search Google, and find a helpful presentation on debugging in Ruby. It turns out debugging the test is as easy as entering

ruby -rdebug test/unit/myclass_test.r

That drops me into the debugger. Now I want to add a breakpoint, so I type

help

which tells me that to add a breakpoint on line 8 of my file, I want this command:

break test/unit/myclass_test.r:8

I could also just type break 8, since I’m only debugging one file, but it’s useful to see how to use filenames. Now I’ve got a breakpoint so I can run ahead to it using the continue command:

cont

This gives me a bunch of errors, so I hit enter again, which means “repeat the last command” which happens to be “continue”.

I get more errors, so I keep hitting enter until it says

Breakpoint 1, test_load at test/unit/myclass_test.rb:8

Super! Now I’m here and I can poke around as I please. Again, the help command is your friend, but what I wanted to do is see what methods were available in the File class. So I used this command:

method File

and the debugger spits back

atime chmod chown ctime flock lstat mtime path truncate

Ahh! I was hoping to see a “basename” method, but it’s not there! Clearly the methods aren’t getting initialized the way I hoped. So, I’m off to do some more debugging.