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)

Tags: , ,

One Response to “More Fun with Times, Mocks, and Closures”

  1. Vivek Vaid Says:

    JSS this is good stuff!