5

I found a bug in the CoC plugin, and I was going to fix the bug, but first I wanted to write some documentation/learning tests as well as a regression test. The question was how: unit testing does not seem to be that much used, apart from the existence of the Vader.vim project.

The piece of code I would like to test does some tests for the existence of some patches in the version of the Vim running (i.e. has('nvim-0.4.0')), but in order to test that in a manner that does not rely on my own version of Vim, I would need to control the has function. Is it possible to override/mock that function (or any other built-in for that matter)? The author of Vader thinks the answer is no.

I could redo this function to do DI of some sorts:

let Local_has = has
if exists("*Testing_has"):
    Local_has = Testing_has
endif
  
if Local_has('nvim-0.4.0'):
  do_something
endif

In Learn VimScript the hard way they mention storing function references, and this seems to be a good beginning:

let MyHas = function("has")
let YourHas = MyHas

echo has('nvim-0.4.3') " => 1
echo MyHas('nvim-0.4.3') " => 1
echo YourHas('nvim-0.4.3') " => 1

The question remains on how to inject the testing version. Just write a test.vim that has created the overrides that then sources the main script?

2
4

I would argue that even if it were possible, it's not a good practice to mock has( in this way. Instead, you should test directly against the versions of vim which you are trying to support. Many vim plugins do have CI pipelines which test the most latest version of vim, vim 8.0, vim 7.4, various neovim versions, etc. Automated testing frameworks in github and gitlab today makes this very easy. Similarly, you can use different vim builds and configurations with and without various features.

The reason I advise this is vim often changes in subtle ways that aren't just "add a feature." Often when a feature, function, command, etc, is added other adjacent things are fixed and altered. It's better to test against a real deployment.

In addition, if you need to test the "exceptional" branch of the has( code, you're better off directly testing the components that you need to use. If the contents of the if has( is really long and complicated, well, generally, it's a bit nicer to refactor the code to allow true "unit" testing than to start mocking things which might break in the next code release.

1
  • Thanks for the good overview. I agree that those are good general rules. In this case, it was an existing plugin with no tests, and I wanted to find a way to test the logic locally by controlling the return values of has(val) without substantially refactoring the original code when doing so, as I don't think large changes would have been accepted. In the end, I created a little POC that essentially pre-processed the plugin source code and and replaced calls to built-ins. Not a thing of beauty, but a fun exercise. – oligofren Feb 25 at 2:59
3

I don't think this is possible either.

I'd say you may found a solution depending on how the call to has() is used. Sometimes you will be able to overwrite the direct consequence of a call to has() (like changing/removing a function/a command... that depends on has() result), other times it wouldn't be possible.

In all cases, if the plugin you want to mock directly uses a builtin function, it's not possible to overwrite this function. At best, you could copy the whole plugin elsewhere, substitute all calls to has() by calls to your#mock#has(), and update the runtime path to use this manually mocked fork.

PS: there exist quite few unit testing frameworks for Vim. I'm even maintaining one.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.