← Back to DevBytes

Zed Testing Integration: Complete Guide

Introduction to Zed Testing Integration

Zed is a high-performance, multiplayer code editor built in Rust by the creators of Atom. One of its most powerful features is its testing integration, which allows developers to run, debug, and iterate on tests directly within the editor without context-switching to a terminal or external tool. This guide walks you through everything you need to know to set up, configure, and master testing workflows inside Zed.

What Is Zed Testing Integration?

Zed's testing integration is a built-in framework that connects the editor to your project's test runner. It parses test results, displays them in a dedicated panel, and lets you trigger tests through commands, keybindings, or inline gutter actions. The integration is powered by Zed's extension system and language servers, which means support varies by language but is growing rapidly.

At its core, the testing integration provides:

Why Testing Integration Matters

Context switching is one of the biggest productivity killers in software development. When you have to jump to a terminal, remember the exact test command, scroll through output, and then jump back to your editor to find the failing line, you lose focus and momentum. Zed's testing integration eliminates this friction by keeping the entire test loop inside the editor.

Beyond convenience, the integration provides visual feedback that is hard to replicate in a terminal. Passing tests show green checkmarks in the gutter, failing tests show red crosses with inline error messages, and you can click directly on a failure to jump to the exact assertion that broke. This tight feedback loop encourages test-driven development and makes refactoring safer.

Prerequisites and Setup

Before diving into the testing integration, make sure you have the following in place:

Installing Language Extensions

Zed's testing support is delivered through language extensions. To install one, open the command palette with Cmd+Shift+P (macOS) or Ctrl+Shift+P (Linux), type zed: extensions, and browse for your language. For example, if you are working with Python, install the Python extension which includes pytest integration.

Project Configuration

Zed uses a .zed directory or a settings.json file in your project root for project-specific configuration. To enable and configure testing, create or edit .zed/settings.json:

{
  "test": {
    "enabled": true,
    "runner": "pytest",
    "watch_on_save": true,
    "panel_position": "bottom"
  }
}

The runner field tells Zed which test framework to use. Supported values include pytest, jest, cargo, go, rspec, and dotnet. The watch_on_save option automatically re-runs tests when you save a file, and panel_position controls where the test panel appears.

Running Tests in Zed

Once your project is configured, you can start running tests. Zed provides multiple entry points for triggering test runs, each suited to different workflows.

Using the Test Panel

Open the test panel by running the test: toggle panel command from the command palette or by using the default keybinding Cmd+Shift+T (macOS) / Ctrl+Shift+T (Linux). The panel displays a hierarchical tree of all discovered tests in your project.

Each node in the tree has a play button that runs that specific test, suite, or file. You can also right-click on any node for additional options like debugging or running with coverage.

Using Inline Gutter Actions

When you open a file containing tests, Zed annotates the gutter next to each test definition with a status icon. A gray circle means the test has not been run yet, a green checkmark means it passed, and a red cross means it failed. Hovering over the icon reveals a play button that runs just that test.

For example, consider this Python test file:

# tests/test_calculator.py
import pytest
from src.calculator import Calculator


class TestCalculator:
    def setup_method(self):
        self.calc = Calculator()

    def test_add(self):
        assert self.calc.add(2, 3) == 5

    def test_subtract(self):
        assert self.calc.subtract(10, 4) == 6

    def test_divide_by_zero(self):
        with pytest.raises(ZeroDivisionError):
            self.calc.divide(1, 0)

In Zed, each of the three test methods will have a gutter icon. Clicking the icon next to test_add runs only that test, while clicking the icon next to TestCalculator runs the entire class.

Using Keybindings and Commands

Zed ships with several default keybindings for test operations. Here are the most important ones:

You can customize these in your keymap file. Open it with zed: open keymap and add entries like this:

[
  {
    "context": "Editor",
    "bindings": {
      "ctrl-t": "test::runAtCursor",
      "ctrl-shift-t": "test::runFile",
      "ctrl-alt-t": "test::rerun"
    }
  }
]

Working with Test Results

After running tests, the test panel updates with results and the editor provides several ways to investigate failures.

Reading the Test Panel

The test panel uses color coding to communicate status at a glance. Green nodes passed, red nodes failed, and yellow nodes were skipped or marked as pending. Collapsed parent nodes show an aggregate status, so if any child failed, the parent appears red.

Clicking on any test in the panel navigates to its definition in the editor. Clicking on a failed test also expands an output section showing the error message, stack trace, and any captured stdout or stderr output.

Inline Failure Display

For failed tests, Zed can display error messages inline directly beneath the failing assertion. This is controlled by the inline_errors setting:

{
  "test": {
    "inline_errors": true,
    "max_inline_lines": 5
  }
}

With this enabled, a failed assertion like assert self.calc.add(2, 3) == 6 would show a red squiggly underline and a floating message below the line:

AssertionError: assert 5 == 6
  +5
  -6

Navigating Between Failures

When multiple tests fail, you can jump between them quickly. Use F4 to go to the next failure and Shift+F4 to go to the previous one. This cycles through every failing test in the project, opening the relevant file and placing the cursor on the failing line.

Configuring Test Runners

Different projects use different test runners, and Zed supports customization for each. Below are configuration examples for popular frameworks.

Python with pytest

For Python projects using pytest, Zed automatically discovers tests by running pytest --collect-only. You can customize the pytest invocation in your settings:

{
  "test": {
    "runner": "pytest",
    "pytest": {
      "command": "python -m pytest",
      "args": ["-x", "--tb=short"],
      "python_path": ".venv/bin/python"
    }
  }
}

The args array lets you pass any pytest flags. The -x flag stops on the first failure, and --tb=short produces shorter tracebacks that are easier to read in the panel.

JavaScript with Jest

For JavaScript and TypeScript projects using Jest, configure the runner as follows:

{
  "test": {
    "runner": "jest",
    "jest": {
      "command": "npx jest",
      "config": "jest.config.js",
      "args": ["--coverage=false"]
    }
  }
}

Here is an example Jest test file that Zed will discover and annotate:

// src/utils/stringUtils.test.js
import { capitalize, reverse } from './stringUtils';

describe('capitalize', () => {
  test('capitalizes the first letter', () => {
    expect(capitalize('hello')).toBe('Hello');
  });

  test('handles empty strings', () => {
    expect(capitalize('')).toBe('');
  });
});

describe('reverse', () => {
  test('reverses a string', () => {
    expect(reverse('abc')).toBe('cba');
  });
});

Rust with cargo test

Rust projects use cargo test as the runner. Zed integrates natively since the editor itself is written in Rust:

{
  "test": {
    "runner": "cargo",
    "cargo": {
      "command": "cargo test",
      "args": ["--no-fail-fast"],
      "manifest_path": "Cargo.toml"
    }
  }
}

The --no-fail-fast flag ensures that all tests run even if some fail, which is useful for getting a complete picture of test health in a single run.

Go with go test

For Go projects, Zed uses go test with JSON output for parsing:

{
  "test": {
    "runner": "go",
    "go": {
      "command": "go",
      "args": ["test", "-json", "./..."]
    }
  }
}

Watch Mode and Continuous Testing

One of the most powerful features of Zed's testing integration is watch mode, which automatically re-runs tests whenever you save a file. This creates a continuous feedback loop where you always know the state of your test suite.

Enabling Watch Mode

You can enable watch mode globally or per-project. For project-level configuration, add this to your .zed/settings.json:

{
  "test": {
    "watch_on_save": true,
    "watch_scope": "affected"
  }
}

The watch_scope setting controls which tests re-run on save. The value affected runs only tests related to the changed file, while all runs the entire suite. The affected option is recommended for large projects where running everything would be too slow.

Manual Watch Toggle

If you prefer not to use watch mode permanently, you can toggle it on demand with the test: toggle watch command. This is useful when you are in the middle of a complex refactor and want continuous feedback, but then want to turn it off during exploratory coding.

Debugging Failed Tests

Zed's testing integration connects with its debugging support to let you debug failing tests without leaving the editor.

Launching a Debug Session

Right-click on any test in the test panel or gutter and select Debug Test. This launches a debug session using your configured debugger. For Python, this uses debugpy; for JavaScript, it uses the built-in Node debugger or Chrome DevTools Protocol.

You need a launch configuration in your project. Here is an example for Python using debugpy:

{
  "debug": {
    "configurations": [
      {
        "label": "Debug pytest test",
        "type": "python",
        "request": "launch",
        "module": "pytest",
        "args": ["${testPath}", "-s"],
        "justMyCode": true
      }
    ]
  }
}

The ${testPath} variable is replaced by Zed with the path to the test file or test ID being debugged. This allows a single configuration to work for any test in your project.

Setting Breakpoints

With the debug session running, you can set breakpoints by clicking in the gutter next to any line of code. When the test execution reaches a breakpoint, Zed pauses and shows the debug panel with the call stack, local variables, and a watch expressions panel. You can step over, step into, and step out using the standard debug controls or their keybindings.

Best Practices

To get the most out of Zed's testing integration, follow these best practices:

Keep Tests Fast

Watch mode is only useful if tests run quickly. If your suite takes more than a few seconds, consider splitting slow integration tests from fast unit tests. You can configure Zed to only watch unit tests:

{
  "test": {
    "watch_on_save": true,
    "pytest": {
      "args": ["-m", "not slow", "--tb=short"]
    }
  }
}

This uses pytest markers to exclude tests marked with @pytest.mark.slow from watch runs while still allowing you to run the full suite manually.

Use Descriptive Test Names

The test panel displays test names, so clear naming makes navigation easier. Prefer full sentences that describe behavior rather than generic names like test_1 or test_case.

# Good: descriptive and readable in the panel
def test_calculator_returns_zero_for_empty_string():
    assert Calculator().add("") == 0

# Bad: unclear in the panel
def test_add_1():
    assert Calculator().add("") == 0

Leverage Test Filtering

Use the search box at the top of the test panel to filter tests by name. This is invaluable in large projects with hundreds or thousands of tests. Typing calculator filters the tree to show only tests with "calculator" in their name or path.

Commit Passing Tests

Use the test panel as a pre-commit checkpoint. Before staging changes, run the full test suite with Cmd+Shift+R and make sure everything is green. This habit catches regressions early and keeps your branch in a deployable state.

Customize for Your Workflow

Every team has different conventions. Take advantage of Zed's flexible configuration to match your existing workflow rather than forcing your workflow to match the tool. If your project uses a custom test script, you can define a custom runner:

{
  "test": {
    "runner": "custom",
    "custom": {
      "command": "./scripts/run-tests.sh",
      "output_format": "jest",
      "working_directory": "${projectRoot}"
    }
  }
}

The output_format field tells Zed how to parse the output. Supported formats include jest, pytest, cargo, go, and junit. If your script produces JUnit XML, use that format for maximum compatibility.

Troubleshooting Common Issues

Tests Not Discovered

If the test panel is empty, first verify that your test runner is installed and accessible from the project root. Open a terminal in Zed with Ctrl+` and run your test command manually. If it works in the terminal but not in the panel, check that the runner setting matches your actual framework and that your test files match the expected naming conventions (e.g., test_*.py for pytest, *.test.js for Jest).

Watch Mode Running Too Often

If watch mode is triggering on unrelated file saves, you can configure file ignore patterns:

{
  "test": {
    "watch_on_save": true,
    "watch_ignore": ["**/node_modules/**", "**/.venv/**", "**/*.md"]
  }
}

Incorrect Test Locations

If clicking a test in the panel jumps to the wrong line, ensure your source maps are correctly configured for compiled languages like TypeScript. Zed relies on source maps to translate between compiled output and source files.

Conclusion

Zed's testing integration brings the entire test-driven development loop inside the editor, eliminating context switches and providing rich visual feedback. By configuring the right runner for your project, enabling watch mode for continuous testing, and leveraging inline failure display and debugging support, you can maintain a fast and reliable development workflow. Start with the basic configuration for your framework, customize keybindings to match your muscle memory, and gradually adopt best practices like test filtering and speed optimization as your project grows. With these tools at your fingertips, writing and maintaining tests becomes a natural, integrated part of your coding process rather than an afterthought.

— Ad —

Google AdSense will appear here after approval

← Back to all articles