Testing Vim Plugins With Vimrunner and RSpec

Vimrunner

Testing Vim plugins is usually a non-trivial task. Many people just don’t do it because they find it either too painful or simply don’t know how to do it efficiently. However, if you like Ruby, there is a great tool that will help you write beautiful, easy to read, end-to-end tests for your Vim projects.

Introducing Vimrunner

Vimmrunner, written by Andrew Radev, is a project that spawns Vim instances using Vim’s client/server functionality and allows you to control them programmatically. It’s written in Ruby and you can find its docs on rubydoc.info. The gem has very intuitive API, and if you are already familiar with the tools in the Ruby ecosystem, you will be up and running in no time. In the next sections I’ll walk you trough writing a simple spec, in order to showcase some of Vimrunner’s abilities.

Getting started

We’ll be testing a simple Vim plugin that replaces “vim” with “Vim” in markdown files:

1
au FileType markdown autocmd BufWritePre <buffer> :%s/vim/Vim/g

To use Vimrunner, you need to install Ruby and your Vim must be compiled with the +clientserver option. For more info, check out Vimrunner’s requirements

The setup

Let’s start by creating a simple structure for our project:

1
2
3
$ mkdir plugin spec
$ touch spec/spec_helper.rb spec/plugin_spec.rb
$ touch Gemfile plugin/sws.vim

The test runner we’ll be using is RSpec. However, you could use minitest or any other runner that fits your needs. Also, bundler will be required to manage our dependencies.

Gemfile:

1
2
3
4
source 'https://rubygems.org'

gem 'rspec'
gem 'vimrunner', '0.3.0'

plugin/sws.vim:

1
au FileType markdown autocmd BufWritePre <buffer> :%s/vim/Vim/g

spec/spec_helper.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'vimrunner'
require 'vimrunner/rspec'

Vimrunner::RSpec.configure do |config|
  # Reuse the Vim server
  config.reuse_server = true

  plugin_path = File.expand_path('.')

  config.start_vim do
    # Use GVim
    vim = Vimrunner.start_gvim

    # Tell Vimrunner to include our plugin
    vim.add_plugin(plugin_path, 'plugin/sws.vim')

    vim
  end
end

Now run bundle install to install our dependencies.

The spec

What we would like to do is edit a markdown file, insert some text that contains “vim”, then save and assert that “vim” has been replaced with “Vim”. Here is how you could accomplish this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'spec_helper'

describe 'sws' do
  it 'replaces vim with Vim in markdown files' do
    # Edit temporary markdown file
    vim.edit!('test.markdown')

    # Insert some text
    vim.insert('vim is awesome. vim vim vim')

    # Save the file
    vim.write

    # Make sure vim has been replaced with Vim
    vim.echo('getline(".")').should eq 'Vim is awesome. Vim Vim Vim'
  end
end

Now, you can run the tests by invoking RSpec:

1
2
3
4
5
6
$ rspec

.

Finished in 0.64155 seconds
1 example, 0 failures

Let’s write an additional spec that ensures our plugin is being activated only when editing makrdown files:

1
2
3
4
5
6
  it "performs substitutions only in markdown files" do
    vim.edit!('test.rb')
    vim.insert('vim.edit! "foo"')
    vim.write
    vim.echo('getline(".")').should eq 'vim.edit! "foo"'
  end

If you run the specs again, you will see that we have 2 passing examples and 0 failures.

That’s it! Vimrunner makes controlling Vim instances a dead simple task. We wrote our first test in no time, and we have the confidence that our plugin is actually working as desired.

The entire source code is available on GitHub - vesln/vim-sws.

Conclusion

In this post we covered how to spawn a Vim instance and control it programmatically in order verify that our plugin works correctly. However, there are many other situations in which you would find Vimrunner useful, testing is just one of them. I encourage you to take a look at the project and spend some time playing with it. I’m sure you’ll come up with great ideas. Cheers!

About the author

Veselin Todorov is a software consultant focused on testing, code quality and building awesome products. He is helping companies to ship their products with confidence and style.

If you are passionate about JavaScript, Node.js, TDD, building and growing applications, you should visit his blog at RobustJS.com