Interview Tip: Checkout Online Code Environments before the Interview
Live coding is a stressful experience. One of the simplest ways to reduce stress around solving a unknown problem in less than an hour is to setup your environment before the interview. My anecdotal experience is that so few developers do this in practice. As an interviewer its a good sign when you check the coding environment before and see the candidate has already setup the environment and got a test up and running.
It’s a signal to the interviewer of several things in the interview:
- You are naturally prepared
- You are able to take full advantage of any tools/libraries provided
- You are pretty motivated to work for this organization
Using CoderPad as an example:
- You can login ahead of time into the pad setup for your interview or at least login to CoderPad’s site and try out a sandbox.
- Select a few of the coding environments you are most comfortable with. Unless you are forced into a specific language always choose your day-to-day language.
- See what libraries you have access to like RSpec, ActiveSupport in Ruby for example.
- Look at the settings for including important things like vim/emac support, Intellisense, etc.
- Get a test running in the environment, so you can TDD right away using your favorite testing framework or at least an available one.
- Test drive a simple problem in the space like FizzBuzz just to get a feel for the environment and how it works.
- If this is the actual coding environment for the interview leave your code in there for the interviewer to discover.
I know I see this level of preperation maybe 5-10% of the time, so it’s an easy way to start off strong.
Quick Inline Vim Keymap Shortcut
I often forget the exact syntax for writing a quick shortcut in in vim/neovim. After hunting for several minutes this morning I made a decision to memorialize the approach. Usually it’s for a simple command like running tests in an Elixir mix console:
So the first n
means it applies just to normal mode. The noremap
means it won’t follow any other mapping so it’s
non-recursive. The ,e
is the what the shortcut is mapped to. And finally the :!mix test<CR>
puts the mix command
into the terminal and hits a carriage return to execute the command.
Aggregate Failures
After working on several large rails codebases in the last 10 years I’ve seen a familiar pattern.
Many tests in Rails projects are integration tests because they rely on actual database objects existing.
One assertion per test is great rule when you don’t have tens of thousands of specs running. :aggregate-failures
allows you to have multiple assertions while still reporting on each failure clearly.
As a bonus it is honored by Rubocop RSpec RSpec/MultipleExpectations
. Not sure why this isn’t
documented better with Rubocop RSpec. Here is the code within the MultipleExpectations
class that
enforces the one assert per spec rule:
And you don’t need to feel guilty about adding aggregate failures:
- It speeds up test runs because it doesn’t do multiple setups
- By default anything that uses ActiveRecord is not a true unit test
- You still get all the errors if multiple lines fail
- Speed of test run on any significant Rails project should almost always win
RSpec Instance Double With Class Names
After many years working with RSpec I discovered a nice little feature and a small gotcha with instance doubles.
I’ve used instance_double
since before it was ported from rspec-fire.
My practice and all the current Relish RSpec examples of instance_double
use the following format:
It turns out the actual instance_double()
method takes a string representing the class or just the class constant:
And the important part here is the parameter:
doubled_class(String, Class)
No String is required here and class constants are auto-verifying. According to a long discussion
on rspec-mocks this behavior exists to avoid some
auto-loading of classes that aren’t needed so that tests can be a bit faster in some cases. For me this breaks
the expectation that I the mock is actually verifying that the class and methods actually exist. If I
wanted just a pure mock I could just use double
. And for Rails projects that make up a lot of the day to day
paid developer work everything auto-loads anyway. Using class constants is just simpler. If you’re on a legacy
project you can probably just add the config to verify the strings ahead of time anyway with the following:
This example code shows what happens when you make a typo in the string constant name and you don’t have the config to verify set:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
require 'spec_helper'
require_relative '../../lib/users/policy_enforcer'
require_relative '../../lib/users/public_policy'
RSpec.describe Users::PolicyEnforcer do
describe '#allowed?' do
let(:policy_enforcer) { Users::PolicyEnforcer.new(public_policy) }
context 'with correct string instance_double class constant' do
let(:public_policy) { instance_double('Users::PublicPolicy') }
before do
allow(public_policy).to receive(:allowed?)
end
it 'returns true' do
policy_enforcer.allowed?
expect(public_policy).to have_received(:allowed?)
end
end
context 'with typo string instance_double class constant' do
let(:public_policy) { instance_double('Use::PublicPolicy') }
before do
allow(public_policy).to receive(:allowed?)
end
it 'lies and returns true' do
policy_enforcer.allowed?
expect(public_policy).to have_received(:allowed?)
end
end
context 'with a proper class constant instance_double' do
let(:public_policy) { instance_double(Users::PublicPolicy) }
before do
allow(public_policy).to receive(:allowed?)
end
it 'returns true' do
policy_enforcer.allowed?
expect(public_policy).to have_received(:allowed?)
end
end
context 'with an typoed class constant instance_double' do
let(:public_policy) { instance_double(User:PublicPolicy) }
before do
allow(public_policy).to receive(:allowed?)
end
it 'fails because the constant is not defined' do
policy_enforcer.allowed?
expect(public_policy).to have_received(:allowed?)
end
end
end
end
The result is the spec with typo string instance double class constant
on line 22 lies to you. It behaves like a plain old double and allows you to accept methods on classes that don’t exist.
More Testable Rake Tasks
A few months ago I had a brief pairing session where I attempted to help another developer with getting some working tests around a rake task they had created. They were having issues testing within rake and executing tasks which is a common issue in rake testing. My suggesion was to build the entire logic of the task in a PORO to avoid the pain of testing rake plumbing.
The approach takes the following steps:
- Create a spec file to test the new class
- Create a class that handles doing the thing
- Create a spec file for the rake task
- Add the rake task and defer everything to the new class
For this example say you have some rake task that should just return the current versions of ruby and bundler locally. Not super useful, but it will involve executing some local commands. We want to be able to run:
And have it dump out:
Instead of diving in and just doing all the logic in rake and having to figure out
how to test it in that context, we start with the class that we want to build the
results, say VersionDisplayer
, and we start with the spec:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require_relative 'version_displayer'
describe VersionDisplayer do
describe '#display' do
let(:version_displayer) { VersionDisplayer.new }
let(:expected_output) do
<<~OUTPUT
Gathering versions
Ruby VERSION: ruby 2.7.0
Bundler VERSION: bundler 2.0
OUTPUT
end
before do
allow(Open3).to receive(:capture2)
.with('ruby --version').and_return(['ruby 2.7.0', nil])
allow(Open3).to receive(:capture2)
.with('bundler --version').and_return(['bundler 2.0', nil])
end
it 'returns the current bundler and ruby version' do
expect { version_displayer.display }.to output(expected_output).to_stdout
end
end
end
This leads us to the VersionDisplayer class that satisfies the spec:
1
2
3
4
5
6
7
8
9
10
11
require 'open3'
class VersionDisplayer
def display
puts "Gathering versions"
ruby_version, _status = Open3.capture2('ruby --version')
bundler_version, _status = Open3.capture2('bundler --version')
puts "Ruby VERSION: #{ruby_version}"
puts "Bundler VERSION: #{bundler_version}"
end
end
Now we can write a simple rake integration spec:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'rake'
describe ':display_versions' do
let(:version_displayer) { instance_double('VersionDisplayer') }
before do
load File.expand_path('Rakefile')
allow(VersionDisplayer).to receive(:new).and_return(version_displayer)
allow(version_displayer).to receive(:display).with(no_args)
end
it 'uses version displayer to output the version information' do
Rake::Task['display_versions'].invoke
expect(version_displayer).to have_received(:display).with(no_args)
end
end
And finally we a simple rake task that delegates all everything:
1
2
3
4
5
6
require_relative 'version_displayer'
desc 'Displays versions of bundler and ruby'
task :display_versions do
VersionDisplayer.new.display
end
I much prefer this to embedding the logic in the rake task and writing complex tests around the rake context.