How to run rails tests in Visual Studio Code
Published: 2022-03-02 14:27:22

Visual Studio is great. It's very slim and quite fast, at least when updated to the M1 version.
The testing support for rails seems to be quite outdated. There is a plugin [Ruby Test Explorer](https://marketplace.visualstudio.com/items?itemName=connorshea.vscode-ruby-test-adapter) but it looks like it needs updating for using the new rails test runner `rails test`. In the end, it's quite simple to actually run tests on your own and have 100% control over it. This will also allow you to rerun the last test, which is a critical productivity feature.

## What you will get

With the method I show here, you can customise your test running as you wish. In the end, you will have the following shortcuts:

**run all tests**: ctrl+t ctrl+a  (*bin/rails test*)
**run all tests in current file**: ctrl+t ctrl+f  (*bin/rails test {file}*)
**run test under cursor**: ctrl+t ctrl+l (*bin/rails test {file:line}*)
**rerun last test**: ctrl+t ctrl+t

## The method

### Create a custom script

To gain the ability to rerun tests, we have to somehow store what we already ran. I am sure, there is an easier way within VSCode, but this method works for sure:

- Create a file `bin/editor-test`
- `touch bin/editor-test && chmod +x bin/editor-test`

Add the following script:

```bash
#!/usr/bin/env bash

# used to run tests with editor and saving the last run so we can repeat.
# for each test run it will save the last args in "tmp/editor-test-last-args".
# usage
# run file:
#   bin/editor-test some_file.rb:12
# run all:
#   bin/editor-test
# run last:
#   bin/editor-test run-last

set -e

if [ "$1" = "run-last" ]; then
  if [[ -f "tmp/editor-test-last-args" ]]; then
    bin/rails test "$(<tmp/editor-test-last-args)"
  else
    echo "No last test saved (file tmp/editor-test-last-args does not exist)"
  fi
else
  echo $@ > tmp/editor-test-last-args
  bin/rails test $@
fi
```

This provides you will the possibility to rerun tests from the console by storing the last test args in *tmp/editor-test-last-args*. Now, lets hook up vscode.

### Setup custom VSCode Tasks

This is a per-project task file, so you could customize this per project.

Run the vscode command (cmd+shift p) "Tasks: Configure Task" and configure the following tasks:

```json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "repeat last test",
            "type": "shell",
            "command": "bin/editor-test run-last",
            "problemMatcher": [],
            "presentation": {
                "clear": true
            },
        },
        {
            "label": "launch tests for project",
            "type": "shell",
            "command": "bin/editor-test",
            "problemMatcher": [],
            "presentation": {
                "clear": true
            },
        },
        {
            "label": "launch tests for current file",
            "type": "shell",
            "command": "bin/editor-test ${file}",
            "problemMatcher": [],
            "presentation": {
                "clear": true
            },
        },
        {
            "label": "launch test for current line",
            "type": "shell",
            "command": "bin/editor-test ${file}:${lineNumber}",
            "problemMatcher": [],
            "presentation": {
                "clear": true
            },
        }
    ]
}
```

### Setup keybindings

To have perfect productivity, lets create some keybindings:

```json
{
    "key": "ctrl+t ctrl+a",
    "command": "workbench.action.tasks.runTask",
    "args": "launch tests for project"
},
{
    "key": "ctrl+t ctrl+f",
    "command": "workbench.action.tasks.runTask",
    "args": "launch tests for current file"
},
{
    "key": "ctrl+t ctrl+l",
    "command": "workbench.action.tasks.runTask",
    "args": "launch test for current line"
},
{
    "key": "ctrl+t ctrl+t",
    "command": "workbench.action.tasks.runTask",
    "args": "repeat last test"
},
```

## Conclusion

There you have it. Super simple, easy to customise test running. This approach will work with every technology, not only rails and no need for plugins.