Skip to content

Tips and Tricks

David Barnett edited this page Aug 14, 2015 · 5 revisions

Writing vroom tests can require some creativity. Vroom has some support for hijacking explicit system calls from vim, but there are several things it can't fake.

Commands and Functions

Vroom doesn't itself have any support for mocking functions or commands. You can override global functions and commands in the vroom test itself and if you use maktaba#test#Override to provide stub implementations for autoload functions.

Conditional Vroom Expectations

If you're testing plugin behavior that varies depending on vim state or system state and you need smart vroom expectations that vary based on the same state, there's currently no ready-made solution for you. The best option is usually to instrument the plugin to force the plugin to behave in a known configuration for testing only. See the section on "Modifying the plugin" below.

Filesystem Access

Filesystem access like filereadable() poses an extra challenge. There's currently no solution to mock it. The best approach is usually to create a temp directory and try to override your plugin to access any files it needs from there.

System Calls

Vroom can expect and stub out system calls, returning a specified stdout and exit code, or even forwarding to a different command and taking stdout/stderr/exit code from there. That gives you a lot of flexibility, but it's not always obvious how to employ those mechanisms to solve specific challenges.

Variable Stdout

A lot of vroom syscall expectations just need to match a pattern and return a given string as stdout:

:DoThing
! doit --now 2>.*
$ Did it!

But sometimes, the string that should be sent to stdout needs to vary depending on unpredictable values passed on the command line:

:DoThing
! doit --at-time [0-9]+ 2>.*
$ Doing it at ???

How do you get the time value from the command and inject it into the stdout text? Vroom lets you capture groups from the command pattern and inject them into the stdout text:

:DoThing
! doit --at-time ([0-9]+) 2>.*
$ Doing it at \1

For more complicated relationships between input and output, there's the (command) control. Combine this with regex captures and you can inject fake syscall implementations with arbitrarily complex behavior:

:DoThing
! doit --at-time-from-file (/tmp/\S+) 2>.*
$ echo -n "Doing it at "; cat \1 (command)

Finally, what if stdout needs to depend on other state outside of the command invocation? You can use environment variables to pass values from vimscript to the command:

:let $VROOM_FOUND_PYTHON = has('python') ? 'Did' : "Didn't"
:DoThing
! doit --now 2>.*
$ echo $VROOM_FOUND_PYTHON (command)

Accessing Stdin

What if you need to test the stdin passed to a command, or use it in subsequent vroom logic? Usually you will want to use environment variables, the (command) control, and temporary files:

:let $VROOM_TEMPFILE = tempname()
:call system('cat > /dev/null', 'King Midas has the ears of an ass!')
! cat > /dev/null
$ cat > $VROOM_TEMPFILE (command)
:echomsg join(readfile($VROOM_TEMPFILE), "\n")
~ King Midas has the ears of an ass!

Maktaba Compatibility Gotcha

Maktaba and vroom have a slight incompatibility you need to work around when using them together. maktaba#syscall# will always try to use /bin/sh for portability, and it will temporarily override if the 'shell' setting is configured to anything else. Vroom overrides the 'shell' setting so it can fake out system calls and inject dummy implementations. To make maktaba respect vroom's override, you need to execute

:call maktaba#syscall#SetUsableShellRegex('\v<shell\.vroomfaker$')

in any vroom file that will trigger maktaba#syscall#.

Writing to Maktaba's Stderr

Even though vroom implements a stderr output channel for syscalls, it doesn't actually do anything useful since vim can't distinguish between stdout and stderr (see #93). maktaba#syscall# distinguishes stderr from stdout by redirecting stderr to a file. If you need to send output to stderr for the purposes of maktaba#syscall# calls, you'll need to detect the file maktaba is redirecting to and write to that file instead:

:call maktaba#syscall#Create('krusty').Call()
! krusty 2>\s*(\S+)
$ echo "Let's just say it moved me…"; echo "TO A BIGGER HOUSE!" > \1 (command)

Modifying the Plugin

Certain design decisions can lead to more testable plugins. For instance, options to disable certain fancy features can help you write simpler tests that focus on just the interesting functionality. Also, smaller composable functions can be easier to test and give you more entry points to override with dummy implementations.

It's perfectly valid to make small modifications to the plugin code solely to facilitate testing. For instance, you could promote script-local functions to autoload functions so you're able to override them from the tests (with maktaba#test#Override). You can also inject fixture data into your plugin with special configuration functions intended to be called only from tests (see maktaba#syscall#SetUsableShellRegex for an example) or, if you're using maktaba, by attaching data to the globals dict in the maktaba.Plugin dict and checking for it in plugin code. Just be careful not to overdo it and drown out the core logic of your plugin with testing fixtures.