Pitfalls of Testing Scripts With Load - Part 2

Check out the full Testing Scripts with Load series: part 1, part 2, part 3

Scope Smashing

Take the following script and spec:

1
2
3
4
5
#!/usr/bin/env ruby

def current_user
  'Bob'
end
1
2
3
4
5
6
7
8
9
10
11
12
13
require 'spec_helper'

def current_user
  'Alice'
end

describe 'Running the server locally' do
  it 'logs that it is running' do
    load 'script/current-user-smash'

    expect(current_user).to eq 'Alice'
  end
end

Alas, the spec fails with:

1
2
expected: "Alice"
     got: "Bob"

This is due to how load works:

If the optional wrap parameter is true, the loaded script will be executed under an anonymous module, protecting the calling program’s global namespace. In no circumstance will any local variables in the loaded file be propagated to the loading environment.

While it is easy to spot the issue this time, that’s not normally the case. Say if the additional method is define by a gem or in supporting file. Or if you are testing multiple scripts that each define the same top-level methods. These conditions will result in very strange and difficult to debug failures. Of course, it’s always a good idea to not define top-level methods to begin with.

Instead always pass the additional wrap parameter. Here I’ve named it as a descriptive inline variable to reveal it’s intent:

1
2
3
4
5
it 'logs that it is running' do
  load 'script/current-user-smash', _in_sandbox = true

  expect(current_user).to eq 'Alice'
end