More Fun with Times, Mocks, and Closures
I solved a complex problem in cruise today with some non-trivial mocking. Check this one out :
I needed to test that every x amount of time, we do a clean checkout. It can be every 6 hours, 2 days, whatever. How do you test this?
Well, maybe you could mock the time.
Time.stubs(:now).returns(Time.now + 2.hours)
That almost works, except that the code works by touching a file. And touch doesn’t use Time.now
. Now at this point, we could go crazy, and mock FileUtils.touch
, of course that means we’ll also have to mock File.exists?
and then what are we really testing?
Instead, I used Mocha to temporarily replace FileUtils.touch
with my own implementation that acts like the original but uses my own value of time. It looks like this so far.
marker = sandbox.root + '/last_clean_checkout_timestamp' now = Time.now FileUtils.stubs(:touch).with(marker).returns(proc do File.open(marker, 'w') {|f| f
you’ll notice that both of these stubs are returning procs that reference now
a local variable….
That’s ruby magic.
It means that I can now forget about mocks and write the rest of my test like this :
@project.do_clean_checkout :every => 1.hour assert @project.do_clean_checkout? assert !@project.do_clean_checkout? now += 59.minutes assert !@project.do_clean_checkout? now += 2.minutes assert @project.do_clean_checkout? assert !@project.do_clean_checkout? @project.do_clean_checkout :every => 2.days now += 1.day + 23.hours assert !@project.do_clean_checkout? now += 2.hours assert @project.do_clean_checkout?
Instead of changing the mocks several times in between each test, I can just change my local variable now
. Because of closures, the mocked out methods return the new value.
Ruby is awesome (and Mocha is pretty sweet too)
May 24th, 2007 at 5:48 am
JSS this is good stuff!