Overview
VS Code has a comprehensive test suite covering unit tests, integration tests, extension tests, and smoke tests. Understanding how to run and write tests is essential for contributing to VS Code.
Unit Tests Test individual components in isolation
Integration Tests Test interactions between components
Extension Tests Test built-in extension functionality
Test Infrastructure
Test Locations
Unit Tests
Integration Tests
Extension Tests
Unit tests are located alongside source files: src/
vs/
editor/
common/
model.ts
test/
model.test.ts # Unit tests
Test Files : *.test.ts in test/ subdirectoriesEntry Point : test/unit/electron/index.jsIntegration tests are marked with .integrationTest.ts: src/
vs/
workbench/
services/
files/
test/
fileService.integrationTest.ts
Test Files : *.integrationTest.tsAlso : Extension tests in extensions/*/out/**/*.test.jsExtension tests are in extension directories: extensions/
markdown-language-features/
out/
test/
*.test.js
Configuration : .vscode-test.js at repository rootEntry Points : Defined per extension in config
Running Unit Tests
Command Line
Linux/macOS
Windows
npm scripts
Run all unit tests: Run specific test file: ./scripts/test.sh --run src/vs/editor/test/common/model/model.test.ts
Filter tests by name: ./scripts/test.sh --grep "should validate range"
Run with debugger: ./scripts/test.sh --inspect-brk
Run all unit tests: Run specific test file: . \ scripts \ test .bat --run src \v s \e ditor \t est \c ommon \m odel \m odel.test.ts
Filter tests by name: . \ scripts \ test .bat --grep "should validate range"
Run Node.js unit tests: Run browser unit tests: Run without Playwright install: npm run test-browser-no-install
Environment Setup
The test script automatically:
Checks for node_modules and runs npm i if needed
Downloads Electron (unless VSCODE_SKIP_PRELAUNCH=1)
Runs tests with Electron binary
Saves crash reports to .build/crashes
Tests run in Electron to test both Node.js and browser APIs in the same environment as VS Code.
Running Integration Tests
Full Integration Test Suite
Linux/macOS
Windows
Individual Suites
./scripts/test-integration.sh
This script runs:
Node.js integration tests (**/*.integrationTest.js)
Extension API tests (folder and workspace)
Colorize tests
Terminal suggest tests
Language feature tests (TypeScript, Markdown, etc.)
Git tests
Standalone tests (CSS, HTML)
. \ scripts \ test-integration.bat
Run specific integration test suites: # Node.js integration tests only
./scripts/test.sh --runGlob ** / * .integrationTest.js
# API tests
npm run test-extension -- -l vscode-api-tests
# Markdown tests
npm run test-extension -- -l markdown-language-features
# Git tests
npm run test-extension -- -l git-base
Test Configuration
Integration tests use special arguments:
API_TESTS_EXTRA_ARGS = "
--disable-telemetry
--disable-experiments
--skip-welcome
--skip-release-notes
--crash-reporter-directory= $VSCODECRASHDIR
--logsPath= $VSCODELOGSDIR
--no-cached-data
--disable-updates
--use-inmemory-secretstorage
--disable-extensions
--disable-workspace-trust
--user-data-dir= $VSCODEUSERDATADIR
"
Extension Tests
Using @vscode/test-cli
The repository uses @vscode/test-cli for running extension tests, configured in .vscode-test.js:
const extensions = [
{
label: 'markdown-language-features' ,
workspaceFolder: 'extensions/markdown-language-features/test-workspace' ,
mocha: { timeout: 60_000 }
},
{
label: 'vscode-api-tests-folder' ,
extensionDevelopmentPath: 'extensions/vscode-api-tests' ,
workspaceFolder: 'extensions/vscode-api-tests/testWorkspace' ,
files: 'extensions/vscode-api-tests/out/singlefolder-tests/**/*.test.js' ,
}
];
Running Extension Tests
Single Extension
All Extensions
From Source
# Run tests for specific extension
npm run test-extension -- -l markdown-language-features
# Run with specific label
npm run test-extension -- -l vscode-api-tests-folder
Available Extension Tests
API Tests
Language Features
Other Extensions
Test the VS Code Extension API: # Single folder tests
npm run test-extension -- -l vscode-api-tests-folder
# Workspace tests
npm run test-extension -- -l vscode-api-tests-workspace
Test Location : extensions/vscode-api-tests/out/Coverage : Commands, languages, workspace, debug API, etc.Test language feature extensions: # Markdown
npm run test-extension -- -l markdown-language-features
# TypeScript (via integration test script)
# Included in ./scripts/test-integration.sh
Standalone language server tests: # CSS
cd extensions/css-language-features/server
npm test
# HTML
cd extensions/html-language-features/server
npm test
Test other built-in extensions: # Git
npm run test-extension -- -l git-base
# Colorize
npm run test-extension -- -l vscode-colorize-tests
# Terminal Suggest
npm run test-extension -- -l terminal-suggest
# Configuration Editing
npm run test-extension -- -l configuration-editing
Web Tests
Test VS Code in browser environments:
Integration Tests
Remote Integration
Browser Unit Tests
./scripts/test-web-integration.sh
Debugging Tests
VS Code Launch Configurations
Use the pre-configured debug configurations:
All Unit Tests
Current File
Extension Tests
Configuration : Debug Unit Tests
Open Run and Debug view (Cmd/Ctrl + Shift + D)
Select “Debug Unit Tests”
Press F5
This runs all unit tests with the debugger attached. Configuration : Debug Unit Tests (Current File)
Open a test file (e.g., model.test.ts)
Select “Debug Unit Tests (Current File)”
Press F5
This runs only the tests in the current file. Individual extension test configurations:
VS Code API Tests (single folder)
VS Code API Tests (workspace)
VS Code Git Tests
VS Code Emmet Tests
Markdown Extension Tests
TypeScript Extension Tests
Select from the debug dropdown and press F5.
Command Line Debugging
Inspect Mode
Chrome DevTools
# Pause at start
./scripts/test.sh --inspect-brk
# Attach debugger
./scripts/test.sh --inspect
Writing Tests
Unit Test Structure
VS Code uses Mocha with a TDD-style interface:
import * as assert from 'assert' ;
import { Model } from 'vs/editor/common/model' ;
suite ( 'Editor Model' , () => {
test ( 'should create model' , () => {
const model = new Model ( 'hello world' , 'text/plain' );
assert . strictEqual ( model . getValue (), 'hello world' );
});
test ( 'should handle edits' , () => {
const model = new Model ( 'hello' , 'text/plain' );
model . applyEdits ([{
range: { startLineNumber: 1 , startColumn: 6 , endLineNumber: 1 , endColumn: 6 },
text: ' world'
}]);
assert . strictEqual ( model . getValue (), 'hello world' );
});
});
Test Organization
Use suite() for grouping
suite ( 'Editor' , () => {
suite ( 'Model' , () => {
test ( 'basic operations' , () => { /* ... */ });
});
suite ( 'View' , () => {
test ( 'rendering' , () => { /* ... */ });
});
});
Use test() for individual tests
test ( 'should do something specific' , () => {
// Arrange
const input = createInput ();
// Act
const result = processInput ( input );
// Assert
assert . strictEqual ( result , expected );
});
Use setup() and teardown()
suite ( 'FileService' , () => {
let service : FileService ;
setup (() => {
service = new FileService ();
});
teardown (() => {
service . dispose ();
});
test ( 'should read file' , async () => {
const content = await service . readFile ( uri );
assert . ok ( content );
});
});
Best Practices
Prefer one comprehensive assertion over multiple precise ones: // Good: Single snapshot-style assertion
test ( 'should transform data' , () => {
const result = transform ( input );
assert . deepStrictEqual ( result , {
id: 1 ,
name: 'test' ,
values: [ 1 , 2 , 3 ]
});
});
// Avoid: Multiple assertions
test ( 'should transform data' , () => {
const result = transform ( input );
assert . strictEqual ( result . id , 1 );
assert . strictEqual ( result . name , 'test' );
assert . strictEqual ( result . values . length , 3 );
});
This makes tests easier to understand and update when requirements change.
Always add tests to existing suites instead of creating new files: // Find the appropriate suite
suite ( 'Editor Model - Existing Suite' , () => {
// Add your test here
test ( 'new test case' , () => {
// ...
});
});
Don’t create a new test file unless testing a new component.
For asynchronous tests: test ( 'should load file asynchronously' , async () => {
const content = await fileService . readFile ( uri );
assert . ok ( content . value . length > 0 );
});
Don’t just test the happy path: test ( 'should throw on invalid input' , () => {
assert . throws (() => {
processInput ( null );
}, /Invalid input/ );
});
test ( 'should handle async errors' , async () => {
await assert . rejects (
async () => await loadFile ( 'nonexistent' ),
/File not found/
);
});
Always dispose of resources: test ( 'should use service' , () => {
const service = new MyService ();
try {
// Test code
assert . ok ( service . doSomething ());
} finally {
service . dispose ();
}
});
// Or use teardown
suite ( 'Service Tests' , () => {
let service : MyService ;
setup (() => {
service = new MyService ();
});
teardown (() => {
service . dispose ();
});
});
Integration Test Example
Integration tests use the full VS Code API:
import * as vscode from 'vscode' ;
import * as assert from 'assert' ;
suite ( 'Markdown Extension Integration Tests' , () => {
test ( 'should provide completions' , async () => {
const doc = await vscode . workspace . openTextDocument ({
content: '# Hello \n\n ' ,
language: 'markdown'
});
const editor = await vscode . window . showTextDocument ( doc );
const position = new vscode . Position ( 1 , 0 );
const completions = await vscode . commands . executeCommand < vscode . CompletionList >(
'vscode.executeCompletionItemProvider' ,
doc . uri ,
position
);
assert . ok ( completions );
assert . ok ( completions . items . length > 0 );
});
});
Smoke Tests
Smoke tests verify basic functionality in a real environment:
Run Smoke Tests
Without Compilation
Custom Build
Smoke Test Location : test/smoke/
Coverage : File operations, editor features, extensions, settings, etc.
CI/CD Test Execution
Tests run automatically in CI/CD:
npm run core-ci # Full test suite
npm run core-ci-pr # PR validation
Runs:
Unit tests
Integration tests
Code hygiene checks
Layer validation
npm run extensions-ci # All extensions
npm run extensions-ci-pr # PR validation
Runs:
Extension tests
Extension compilation
Extension hygiene
Troubleshooting
Tests Fail Locally but Pass in CI
Check Node.js version matches CI
Ensure clean git state: git clean -xfd
Rebuild: npm run compile
Check for OS-specific issues
Increase timeout in test:
test ( 'slow test' , async function () {
this . timeout ( 10000 ); // 10 seconds
// ...
});
Check for deadlocks or infinite loops
Run with --inspect-brk to debug
Ensure extension is compiled: npm run watch-extensions
Check workspace folder exists
Verify extension activation events
Check logs in .build/logs/integration-tests
Cannot Find Module Errors
Run npm install
Clear cache: rm -rf out node_modules/.cache
Recompile: npm run compile
Next Steps
Debugging Learn how to debug tests
Development Workflow Understand the development process
Building from Source Review build instructions
Mocha Documentation Learn more about Mocha testing framework