Setting Up Your Testing Environment
In this lesson, you'll set up a testing environment using Vitest and understand why testing is a strategic investment for your Sanity Studio.
With Sanity's code-first architecture, your Studio is configured through the code you write—custom inputs, validation functions, preview configuration, formatting helpers, and other business logic. Testing this code before it reaches Studio gives you confidence that your changes won't break the editing experience.
When you write tests for your Studio code, you're not checking that it runs—you're encoding your team's business requirements and design decisions. Tests become documentation of how your Studio should work, written in code that can verify itself.
Consider a concert venue booking system. If you write a validation rule that certain event types must have a venue, a test ensures this rule works correctly:
- When a concert event has no venue, validation fails with a helpful message
- When a livestream event has no venue, validation passes (it doesn't need one)
- When someone modifies the validation logic later, the test catches breaking changes
This is powerful when developing new features: write tests that describe the expected behavior first, then implement the code that makes them pass. Tests become executable specifications that document your business logic.
Testing fits into your development workflow at multiple points:
- During local development - Watch mode provides instant feedback as you write code
- In pull requests - Automated CI runs validate changes before code review
- Before deployment - Tests ensure your changes won't disrupt content editors
By implementing a testing strategy, you can iterate on your Studio with confidence, knowing that your custom inputs, schema helpers, and validation functions are covered by tests.
Vitest is a modern testing framework designed for TypeScript projects. It provides a fast, developer-friendly experience with instant feedback through watch mode.
This repository is a monorepo with multiple apps (apps/studio, apps/tickets, apps/web). Vitest's workspace feature lets you run tests across all apps from the root, or target individual apps.
First, install Vitest at the root level:
npm install -D -w vitestThe -w flag installs to the workspace root, making Vitest available to all packages.
Create a vitest.config.ts file at the repository root:
import {defineConfig}from 'vitest/config'
export default defineConfig({ test: { // Automatically discover test configs in all apps projects: ['apps/*'], },})This tells Vitest to look for test configurations in each app directory. Each app can have its own specialized config.
defineConfig() directly in your Studio's vitest.config.ts.Now create apps/studio/vitest.config.ts:
import {defineProject} from 'vitest/config'
export default defineProject({ test: { name: 'studio', include: ['**/*.test.ts'], environment: 'node', },})Notice we use defineProject() instead of defineConfig(). This provides better type checking for workspace projects. The name: 'studio' is required—Vitest needs unique names for each project.
Add test scripts in the root package.json:
{ "scripts": { "test": "vitest", }}Now you can run tests from the root (all apps) or run individual app tests.
Let's write a simple test to verify your setup works. Create a test file at apps/studio/example.test.ts:
import {describe, it, expect} from 'vitest'
describe('Vitest setup', () => { it('runs basic assertions', () => { expect(2 + 2).toBe(4) })
it('handles string comparisons', () => { const greeting = 'Hello, Sanity' expect(greeting).toContain('Sanity') })
it('validates arrays', () => { const events = ['concert', 'livestream', 'exhibition'] expect(events).toHaveLength(3) expect(events).toContain('concert') })})This test file demonstrates the basic structure:
describe- Groups related tests together (think of it as a container)it- Defines an individual test case (read it as "it should...")expect- Makes assertions about values (this is where actual testing happens)
Run the tests. Since we're in a monorepo, you have multiple options:
# From the repository root - runs all tests in all appspnpm test
# From the root - run only Studio testspnpm test --project=studioFor now, the simplest approach is to run from the root with pnpm test.
apps/studio/example.test.ts file with the code above and run pnpm test to verify your setup works.You should see output indicating all three tests passed. Vitest enters watch mode, waiting for file changes to rerun tests automatically.
Each test follows a three-step pattern:
- Arrange - Set up the data and conditions
- Act - Execute the code being tested
- Assert - Verify the result matches expectations
Here's an example with the event domain:
describe('Event type classification', () => { it('identifies concerts as venue-required events', () => { // Arrange const eventType = 'concert' const venueRequiredTypes = ['concert', 'exhibition']
// Act const requiresVenue = venueRequiredTypes.includes(eventType)
// Assert expect(requiresVenue).toBe(true) })})This pattern keeps tests readable and maintainable. When a test fails, you can quickly identify which stage failed.
Not everything needs a test. Focus on code that contains business logic or could break in surprising ways:
Write tests for:
- Validation functions that enforce business rules
- Helper functions that transform or format data
- Custom input components with complex interactions
- Preview configurations that shape how content appears
Don't test:
- Simple field definitions with no logic
- Third-party library code (assume it's tested)
- Trivial getters and setters with no transformation
For this events Studio, high-value tests would cover:
- Validation: "Concert events must have a venue, livestream events don't"
- Date logic: "Doors open time calculates correctly from event date"
- URL validation: "Ticket URLs must be valid HTTPS URLs"
Leave pnpm test running while you develop. Vitest watches your files and automatically reruns affected tests when you save changes.
Try modifying your test file—change an assertion to make it fail:
expect(2 + 2).toBe(5)Vitest immediately detects the change and shows you the failure. Change it back to 4 and the tests pass again. This instant feedback loop helps you catch errors early and iterate quickly.
You now have a working test environment and understand why testing is a strategic investment for Studio development. You've set up Vitest in a monorepo configuration with workspace projects, learned the basic structure of tests with describe, it, and expect, and discovered how watch mode provides instant feedback as you develop. You also know what types of code are worth testing—validation functions, helper utilities, and custom components with business logic—versus what to skip, like simple field definitions with no logic. In the next lesson, you'll test validation logic and access control rules from the events Studio, building confidence with real-world examples.
In the next lesson, you'll test validation logic and access control rules from the events Studio, building confidence with real-world examples.