Vitest Testing Framework

repository·main·Indexed 12 days ago

https://github.com/vitest-dev/vitest

A Vite-native testing framework providing a fast, modern experience for unit, component, and browser testing with a Jest-compatible API. Version 5.0.0-rc.1 includes support for Hot Module Replacement (HMR), OpenTelemetry tracing, and specialized packages for browser providers (Playwright, WebdriverIO, Preview), snapshot testing, and Web Worker simulation.

Tokens
331.4K
Snippets
1.2K
Records
1.4K
Agent score
94%

What's inside Vitest

  1. What is Vitest?

    main
    Vitest is a next-generation testing framework powered by Vite. It is designed to be fast and compatible with the Vite ecosystem, allowing you to use the same configuration, transformers, resolvers, and plugins from your application in your tests. It provides a Jest-compatible API, making it easy to migrate from Jest while benefiting from Vite's speed and features like HMR-style watch mode.
  2. Component Testing in Vitest

    main

    Component testing focuses on verifying individual UI components in isolation. It sits between unit tests and end-to-end tests, providing faster feedback and easier debugging than E2E tests while offering more accurate environments than DOM simulations when used with Browser Mode.

    Vitest supports multiple frameworks including Vue, React, Svelte, Lit, Preact, Qwik, Solid, Marko, and more.

    Component Testing Hierarchy

    When planning your test suite, prioritize coverage in this order:

    1. Critical User Paths: Always test these.
    2. Error Handling: Test failure scenarios.
    3. Edge Cases: Empty data, extreme values.
    4. Accessibility: Screen readers, keyboard navigation.
    5. Performance: Large datasets, animations.
  3. Key features of Vitest

    main

    Vitest includes a wide range of built-in features for modern web development:

    • Vite Integration: Uses your existing Vite config, transformers, and plugins.
    • Assertion Styles: Built-in Chai assertions with Jest expect compatibility.
    • Watch Mode: Smart and instant watch mode similar to HMR.
    • Code Coverage: Native coverage via v8 or istanbul.
    • Mocking: Jest-compatible mocking, stubbing, and spies.
    • DOM Mocking: Support for JSDOM and happy-dom.
    • Browser Mode: Run component tests (Vue, React, Svelte, Lit, Marko) natively in the browser.
    • Type Testing: Support for expect-type for type-level testing.
    • Advanced Testing: Benchmarking (via Tinybench), Projects support, Sharding, and ESM-first architecture with top-level await.
  4. Choose the right Vitest browser package

    main

    The @vitest/browser package is specifically designed for developers who want to create their own custom browser provider.

    If your goal is simply to run your existing tests in a real browser environment, you should use one of the following pre-built packages instead:

    • @vitest/browser-playwright: Use this to run tests using Playwright.
    • @vitest/browser-webdriverio: Use this to run tests using WebdriverIO.
    • @vitest/browser-preview: Use this to visualize how your tests appear in a real browser.
  5. How project configuration resolution works

    main

    When using Vitest projects, configuration is resolved through a hierarchy of inputs.

    Root Configuration Priority

    The root configuration is resolved in this order (highest priority last):

    1. The root config file
    2. viteOverrides (merged on top of the config file)
    3. CLI options (options, applied on top of everything else)

    Project Inheritance

    • Config files/directories: Resolve only their own file and do not inherit from the root configuration.
    • Inline projects: Inherit the root configuration by default. The root config file is re-executed, viteOverrides are merged, and then the project's own options are merged.
    • extends: false: The inline project resolves only its own options.
    • extends: './path': The referenced file is re-executed instead of the root config, and viteOverrides are not merged.

    Exclusions from Inheritance

    The following options from viteOverrides are never inherited by projects:

    • plugins: Plugin instances in viteOverrides belong to the root server and cannot be shared.
    • test.browser: Describes instances of a single project.
    • test.tagsFilter: Applies to the whole run.
    • name and projects.
    • globalSetup (as it runs once per test run).

    Global Options

    Regardless of inheritance, these groups reach every project:

    • CLI options for test execution: --testTimeout, --retry, --pool, etc.
    • Run-level options: coverage, attachmentsDir, and mergeReportsLabel from the root's resolved config.
  6. Understand the limitations of the Browser Preview provider

    main

    The preview provider is designed for visual inspection of tests in a real browser, but it lacks the automation depth of providers like Playwright or WebdriverIO.

    Key limitations include:

    • No Headless Mode: The browser window will always be visible.
    • Single Instance Constraint: You cannot run multiple instances of the same browser; each instance in the instances array must use a different browser type.
    • Limited Configuration: You can only specify the browser name; advanced browser capabilities or options are not supported.
    • No Low-Level Control: It does not support CDP (Chrome DevTools Protocol) commands or other low-level interactions.
    • Interactivity API: The userEvent API is a re-export from @testing-library/user-event and does not feature special integration with the browser instance.
  7. Use type-aware lifecycle hooks with test.extend

    main

    When using test.extend to add custom context, you can now reference lifecycle hooks like beforeEach and afterEach directly on the returned test object. These hooks are aware of the extended context provided by the extension.

    import { test as baseTest } from 'vitest'
    
    const test = baseTest.extend<{ 
      todos: number[] 
    }>({
      todos: async ({}, use) => {
        await use([])
      },
    })
    
    // These hooks are aware of the extended context
    test.beforeEach(({ todos }) => {
      todos.push(1)
    })
    
    test.afterEach(({ todos }) => {
      console.log(todos)
    })
  8. Manage nested suite hook execution

    main

    When using nested describe blocks, Vitest follows a hierarchical pattern where parent hooks wrap child hooks.

    The pattern follows:

    • Parent aroundAll wraps the entire suite (including children).
    • Parent aroundEach wraps child aroundEach hooks.
    • Parent beforeEach wraps child beforeEach hooks.
    • Child hooks execute, then parent afterEach hooks execute.
    • Finally, parent afterAll and aroundAll cleanup occur.
    describe('outer', () => {
      aroundAll(async (runSuite) => {
        console.log('outer aroundAll before')
        await runSuite()
        console.log('outer aroundAll after')
      })
    
      beforeAll(() => console.log('outer beforeAll'))
    
      aroundEach(async (runTest) => {
        console.log('outer aroundEach before')
        await runTest()
        console.log('outer aroundEach after')
      })
    
      beforeEach(() => console.log('outer beforeEach'))
    
      test('outer test', () => console.log('outer test'))
    
      describe('inner', () => {
        aroundAll(async (runSuite) => {
          console.log('inner aroundAll before')
          await runSuite()
          console.log('inner aroundAll after')
        })
    
        beforeAll(() => console.log('inner beforeAll'))
    
        aroundEach(async (runTest) => {
          console.log('inner aroundEach before')
          await runTest()
          console.log('inner aroundEach after')
        })
    
        beforeEach(() => console.log('inner beforeEach'))
    
        test('inner test', () => console.log('inner test'))
    
        afterEach(() => console.log('inner afterEach'))
        afterAll(() => console.log('inner afterAll'))
      })
    
      afterEach(() => console.log('outer afterEach'))
      afterAll(() => console.log('outer afterAll'))
    })
  9. What are Vitest pools and how do they work?

    main

    Vitest runs tests within a 'pool'. A pool manages the execution environment and isolation for your tests. Vitest provides several built-in pool runners:

    • threads: Uses node:worker_threads for isolation via a new worker context.
    • forks: Uses node:child_process for isolation via a new child_process.fork process.
    • vmThreads: Uses node:worker_threads but provides isolation using the vm module instead of a new worker context.
    • browser: Runs tests using browser providers.
    • typescript: Runs typechecking on tests.

    Custom pools are an advanced, experimental, and low-level API primarily intended for library authors who need to control the lifecycle and communication of test workers.

  10. Understand the Benchmark Provider lifetime

    main

    Vitest imports the provider module on its first use and caches the default export for the entire lifetime of the worker.

    Because there are no explicit setup or teardown hooks in the BenchmarkProvider API, any state that needs to persist across multiple benchmark runs should be stored directly on the provider object itself (worker-scoped state).

  11. Compare watchTriggerPatterns and forceRerunTriggers

    main

    Both watchTriggerPatterns and forceRerunTriggers allow you to rerun tests when files outside the import graph change, but they behave differently:

    • watchTriggerPatterns: Reruns only the specific tests you map for a given pattern. This is more efficient and keeps the watch loop fast.
    • forceRerunTriggers: Reruns every test in your project whenever any file matching the pattern changes.

    Use watchTriggerPatterns when you can precisely identify which tests depend on which files, and use forceRerunTriggers for broader, less specific dependencies.