Vim: Switch Between Tests (Or Other Files)

As most people I am annoyed with repeating stuff over and over. And today my workflow about switching between test files pissed me off. So I wrote a little vim function that switches between unit tests and their corresponding SUTs. It also creates them if non-existent.

I’m currently working on a Symfony project that uses Codeception for its testing. Codeception puts all tests into a ‘tests/’ directory in your project root. might be unit, functional or acceptance, i.e. ‘tests/unit/..’.

Example structure:

projectroot/
- src/your/bundle/...
- tests/unit/src/your/bundle/...
- tests/functional/src/your/bundle/...
- tests/acceptance/src/your/bundle/...

By convention the directory structure under the particular tests path mirrors the actual code directory structure.

The vim function I wrote acts upon this structure. But it should be easy to rewrite the function for your project’s structure, testing tool or language.

Below the function is my example mapping for switching between tests. In my case that’s pressing ‘,tu’ for switching between unit tests and the class files.

File/Path creation

If the file and/or the path to the file doesn’t exist the function will create both path and file.

Enough said - here’s the function:

" Parameters:
"
" fileExtension: the file extension this script should act on, i.e. 'php'
" (without dot)
"
" firstDirBeginning: a file path that identifies the first type
" of paths, i.e. 'tests/unit/'
"
" secondDirBeginning: a file path pattern that identifies the second type
" of paths, i.e. 'src/'
"
" filenameAddition: string that should be removed from the first filename and
" added to the second, i.e. 'Test' if your testfile filename has the suffix
" 'Test' as in /path/MyServiceTest.php

function! SwitchBetweenFiles(fileExtension, firstDirBeginning, secondDirBeginning, filenameAddition)
    let f = bufname("%")
    if f =~ '.'.a:fileExtension
        if f =~ '\<'.a:firstDirBeginning && f =~ a:filenameAddition.'\.'.a:fileExtension
            let filename = substitute(substitute(f, a:firstDirBeginning, '', ''), a:filenameAddition, '', '')
            if !filereadable(filename)
                let new_dir = substitute(filename, '/\w\+\.'.a:fileExtension, '', '')
                exe ":!mkdir -p ".new_dir
            endif
            exe ":e ".filename
        elseif f =~ '\<'.a:secondDirBeginning && f !~ a:filenameAddition.'\.'.a:fileExtension
            let filename = substitute(substitute(f, a:secondDirBeginning, a:firstDirBeginning.a:secondDirBeginning, ''), '.'.a:fileExtension, a:filenameAddition.'.'.a:fileExtension, '')
            if !filereadable(filename)
                let new_dir = substitute(filename, '/\w\+'.a:filenameAddition.'\.'.a:fileExtension, '', '')
                exe ":!mkdir -p ".new_dir
            endif
            exe ":e ".filename
        else
            echom "Could not switch because needed patterns not matched."
        endif
    endif
endfunction

nmap <leader>tu :call SwitchBetweenFiles('php', 'tests/unit/', 'src/', 'Test')<cr>
" open in new split
nmap <leader>tsu <c-w>v:call SwitchBetweenFiles('php', 'tests/unit/', 'src/', 'Test')<cr>
nmap <leader>tf :call SwitchBetweenFiles('php', 'tests/functional/', 'src/', 'Test')<cr>
nmap <leader>ta :call SwitchBetweenFiles('php', 'tests/acceptance/', 'src/', 'Test')<cr>

I hope you’ll profit from it.

comments powered by Disqus