Awkward and Proud

Faster TDD feedback with tmux, tslime.vim, and turbux.vim

tl;dr Developing in a tmux session has sped up my TDD cycle considerably, especially with the help of tslime.vim and turbux.vim. I get immediate feedback in one pane, but never have to leave vim or lose context of what I’m working on to see the test result.

Taking a cue from Xavier Shay’s excellent intro to tmux, I’ve been using tmux lately as my primary workspace. There are excellent introductions to tmux elsewhere, but I’ve really enjoyed the switch from MacVim/Terminal to a single tmux session for development. But rather than sing tmux’s praises, I’d like to talk about how tmux and a vim plugin have changed my testing feedback loop for the better.

Other test-running solutions

Autotesting gives you immediate feedback, but runs everytime you save a file. Even though this often is desired behavior, I can’t tell you how many times I’ve saved a feature file, only to immediately notice a typo. Especially with Rails project, this can be an expensive amount of time. I end up feeling punished for saving my work.

I’ve also tried more editor-embedding techniques of running tests. Both rails.vim and rake.vim provide facilities for running :Rake. When combined with a keyboard shortcut, this gets closer to the kind of control I like to have, running my tests exactly when I want them. The downside, though, is that I lose control of my editor and have to wait for the command to finish before I can type, or even navigate again. And I can’t look at a failure message and my code at the same time.

A faster feedback loop

A practice that is quickly gaining popularity in the Ruby community is isolating your business logic from your persistance logic and framework. Rather than load Rails (or some other large library or framework), you sequester all business logic in its own class or module, and then test that class or module in isolation. This has a ton of benefits for the longevity of your code, but one of the side benefits is the speed increase for running individual specs or tests. This technique is being championed by Gary Bernhardt and Corey Haines, among others.

Because tmux is so scriptable, it isn’t hard to send commands to other panes in a tmux session programmatically. Leveraging the power of rails.vim and tslime.vim, I’ve created a vim plugin that shortens the feedback loop when practicing TDD in a tmux session. It’s called turbux.vim.

Using tslime.vim

My typical workflow now involves setting up a tmux session for my project, splitting vertically (<C-b> %), and using layout 4 (<C-b> <Alt-4>). In fullscreen, the result is about 30% for my shell on the left, and 70% for vim on the right.

The first time you use it, tslime.vim will prompt you to input your tmux session name, window number, and pane number. There is completion for each of these prompts, so you can happily mash <Tab>.

The plugin exposes a general-purpose function to send arbitrary text to the configured tmux pane. For example, you can use it in the following way:

:call Send_to_Tmux("rspec ".expand("%")."\n")

The above command would send rspec path/to/spec.rb to the configured pane. For me, this pattern of running the test file that is currently open is so common that I’ve packaged up some useful defaults in turbux.vim.

Using turbux.vim

Turbux.vim tries to intelligently choose the right spec, test or feature to run when you invoke it. If you’re in a spec, invoking the plugin (by default with <leader>t) will run rspec path/to/my_spec.rb in the corresponding pane. In a test-unit file, it will run ruby -Itest path/to/test.rb. In a cucumber feature file, it will run cucumber path/to/my.feature.

Thanks to rails.vim’s awesomeness, I’ve also provided some mappings for when the current file has a corresponding test or spec. For example, When I’m in a file that has a corresponding spec, such as a model or controller, the command will run the that spec.

Finally, if the plugin is invoked outside the context of any feature or spec-related file, it will simply run the most recent test command again.

Also, I’ve added a mapping for <leader>T to run a more focused spec or cucumber scenario. It works by adding the vim cursor’s line number to the rspec or cucumber command.

Conclusion

This setup has been really rewarding so far. There’s far less context switching, as I never have to leave my editor. There are also fewer surprises. As far as I’m concerned, the faster my feedback, the better.

Note: You will probably want to use my fork of tslime.vim, as the main repository has some outstanding bugs, and the fixes have yet to been merged in.

Bonus

See the plugin in action. If the video is hard to see, visit vimeo. There is a link to download at full size.