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:

:nnoremap ,e :!mix test<CR>

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:

MultipleExpectations

def on_block(node)
  return unless example?(node)

  return if example_with_aggregate_failures?(node)

  expectations_count = to_enum(:find_expectation, node).count

  return if expectations_count <= max_expectations

  self.max = expectations_count

  flag_example(node, expectation_count: expectations_count)
end

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:

let(:public_policy) { instance_double('Users::PublicPolicy') }

It turns out the actual instance_double() method takes a string representing the class or just the class constant:

instance_double(doubled_class)

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:

config.mock_with :rspec do |mocks|
  mocks.verify_doubled_constant_names = true
end

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.

Users::PolicyEnforcer
  #allowed?
    with correct string instance_double class constant
      returns true
    with typo string instance_double class constant
      lies and returns true
    with a proper class constant instance_double
      returns true
    with an typoed class constant instance_double
      fails because the constant is not defined (FAILED - 1)

Failures:

  1) Users::PolicyEnforcer#allowed? with an typoed class constant instance_double fails because the constant isn not defined
     Failure/Error: let(:public_policy) { instance_double(User:PublicPolicy) }

     NameError:
       uninitialized constant PublicPolicy
     # ./spec/users/policy_enforcer_spec.rb:49:in `block (4 levels) in <top (required)>'
     # ./spec/users/policy_enforcer_spec.rb:52:in `block (4 levels) in <top (required)>'

Finished in 0.0175 seconds (files took 0.45833 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/users/policy_enforcer_spec.rb:55 # Users::PolicyEnforcer#allowed? with an typoed class constant instance_double fails because the constant isn not
 defined

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:

rake display_versions

And have it dump out:

Gathering versions
Ruby VERSION: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
Bundler VERSION: Bundler version 2.1.4

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.

Configuring Jenkins Properties As Code

Jenkins appears to be the single most popular CI/CD tool despite its age and corresponding legacy tradeoffs. When I landed on the State of California’s Child Welfare project Jenkins was already the default option. I brushed up on the new features since I had largely missed out on the new pipeline features with an actual code as configuration option that was added to Jenkins around 2016.

We moved to the state’s infrastructure and Jenkins server by making heavy use of Jenkinsfiles and a few triggering plugins like Github Pull Request Builder and the Generic Webhook Trigger. Later in the project, I volunteered to lead the pipeline team to ‘Automate All the Things’. Eventually, we experimented with writing Jenkins Shared Libraries which is a somewhat clunky way to share code between Jenkinsfiles without breaking down and writing whole Jenkins plugins. After some early successes, we eventually moved to write a significant library of shared steps which were then used on dozens of the organization’s projects.

After a time it occurred to me that we could embed many of the settings in the projects that were a pain to set up in the GUI from within a shared library custom step. One of the most elaborate plugins to configure was Github PullRequestBuilder, but we were eventually able to capture all of its settings in a fairly simple step.

node('buildNode') {
  triggerProperties = githubPullRequestBuilderTriggerProperties()
  properties([
    pipelineTriggers([triggerProperties])
  ])
  ...
}

Under the covers it sets up an Frankenstien sort of object:

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
[$class: 'org.jenkinsci.plugins.ghprb.GhprbTrigger',
  spec: 'H/5 * * * *',
  configVersion: 3,
  allowMembersOfWhitelistedOrgsAsAdmin: true,
  orgslist: 'ca-cwds',
  cron: 'H/5 * * * *',
  onlyTriggerPhrase: false,
  useGitHubHooks: true,
  permitAll: false,
  autoCloseFailedPullRequests: false,
  displayBuildErrorsOnDownstreamBuilds: false,
  triggerPhrase: 'retest this please',
  skipBuildPhrase: '.*\\[skip\\W+ci\\].*',
  extensions: [
                [
                    $class: 'org.jenkinsci.plugins.ghprb.extensions.build.GhprbCancelBuildsOnUpdate',
                    overrideGlobal: false
                ],
                [
                    $class: 'org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus',
                    commitStatusContext: 'Pull Request Testing',
                    statusUrl: 'https://jenkins.example.com',
                    addTestResults: false,
                    completedStatus: [
                      [
                        $class: 'org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage',
                        message: 'Success',
                        result: 'SUCCESS'
                      ],
                      [
                        $class: 'org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage',
                        message: 'Build Failed',
                        result: 'FAILURE'
                      ],
                      [
                        $class: 'org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage',
                        message: 'Build Error',
                        result: 'ERROR'
                      ]
                    ]
                  ]
              ]
    ]

This saved significant time for any team trying to set up their first pull-request based build in their Jenkinsfile, as they no longer had to figure out all of the boxes to check and text fields to fill out. So if you’re just getting started trying to reuse code among Jenkinfiles don’t forget the option of embedding complex config as well.

Notes:

We’ve pulled in three things now Jenkins Github plugin, GHPRB, Generic Webhook Trigger. Had to deal with them as steps. Eventual goal is to move all config out of Jenkins into code. Unfortunate bootstrap issue especially with triggers, or a first time project. Jenkins X is supposed to provide some of this functionality.