# How to Write Vitest Tests Co-Located with Components in OpenHuman

> Learn how to write Vitest tests co-located with components in OpenHuman. Discover the simple process of placing test files next to your components for better organization and faster development.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-06-16

---

**To write Vitest tests co-located with components in OpenHuman, place a `*.test.tsx` file in the same directory as your component and ensure it matches the glob pattern `src/**/*.test.{ts,tsx}` defined in [`app/test/vitest.config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/test/vitest.config.ts).**

The OpenHuman repository adopts a **co-located testing strategy** for its frontend workspace that keeps unit tests side-by-side with the React components they verify. This approach simplifies relative imports and ensures that tests remain tightly coupled to the implementation they cover, following the behavioral testing philosophy documented in the project's knowledge base.

## Understanding the Co-Located Test Pattern

In OpenHuman, every component under `app/src/…` has a corresponding unit test in the same folder, sharing the same base name but with a [`.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/.test.tsx) suffix. For example, [`Button.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/Button.tsx) pairs with [`Button.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/Button.test.tsx) in `app/src/components/ui/`. This convention eliminates deep import paths and keeps related files discoverable.

The repository also supports placing tests in a `__tests__` subdirectory within the component's folder, as demonstrated by [`app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx) testing [`app/src/components/skills/SkillsExplorerTab.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/components/skills/SkillsExplorerTab.tsx).

## Configuring Vitest for Co-Located Tests

The Vitest configuration file at [`app/test/vitest.config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/test/vitest.config.ts) enforces the co-location pattern through a specific glob pattern. The configuration includes `src/**/*.test.{ts,tsx}`, which automatically discovers test files nested within the source tree regardless of their specific directory depth.

Coverage reports are configured to include only component source code under `src/**/*.{ts,tsx}` while explicitly excluding test files from coverage calculations. Global mocks are initialized once in [`app/src/test/setup.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/test/setup.ts), with `clearMocks` set to `true` but `mockReset` set to `false` to preserve mock implementations across tests.

## Writing a Co-Located Component Test

### File Naming Convention

Follow the strict naming pattern: [`ComponentName.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/ComponentName.test.tsx). The test file must reside in the same directory as the component it tests (e.g., `app/src/components/ui/`) or within a `__tests__` subdirectory. This proximity enables simple relative imports (`./Button`) and immediate access to directory-scoped helper utilities.

### Test Structure and Setup

Tests import the component using relative paths and utilize **Testing Library** (`@testing-library/react`) for rendering. Queries should target accessible roles and behavioral attributes rather than implementation details. Use Vitest's `expect` with helpers like `toBeInTheDocument`, `toHaveAttribute`, and `toMatch` for assertions.

### Complete Example: Button Component

Here is a minimal implementation and corresponding test from the OpenHuman source code, demonstrating the co-located pattern:

```tsx
// File: app/src/components/ui/Button.tsx
import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from 'react';

export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  leadingIcon?: ReactNode;
  trailingIcon?: ReactNode;
}

const BASE = 'inline-flex items-center justify-center gap-2 ...';
const VARIANTS: Record<ButtonVariant, string> = { /* … */ };
const SIZES: Record<ButtonSize, string> = { /* … */ };

const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
  const {
    variant = 'primary',
    size = 'md',
    leadingIcon,
    trailingIcon,
    className,
    type,
    children,
    ...rest
  } = props;

  const classes = [BASE, VARIANTS[variant], SIZES[size], className ?? '']
    .filter(Boolean)
    .join(' ');

  return (
    <button ref={ref} type={type ?? 'button'} className={classes} {...rest}>
      {leadingIcon}
      {children}
      {trailingIcon}
    </button>
  );
});

Button.displayName = 'Button';
export default Button;

```

```tsx
// File: app/src/components/ui/Button.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import Button from './Button';

describe('Button', () => {
  it('renders children and defaults to primary/md with type=button', () => {
    render(<Button>Send</Button>);
    const btn = screen.getByRole('button', { name: 'Send' });
    expect(btn).toBeInTheDocument();
    expect(btn).toHaveAttribute('type', 'button');
    expect(btn.className).toMatch(/bg-primary-500/);
    expect(btn.className).toMatch(/h-9/);
  });

  it('applies secondary variant classes', () => {
    render(<Button variant="secondary">Cancel</Button>);
    const btn = screen.getByRole('button', { name: 'Cancel' });
    expect(btn.className).toMatch(/border-neutral-300/);
    expect(btn.className).toMatch(/bg-neutral-0/);
  });
});

```

## Testing Philosophy and Best Practices

According to the GitBook documentation in [`gitbooks/developing/testing-strategy.md`](https://github.com/tinyhumansai/openhuman/blob/main/gitbooks/developing/testing-strategy.md), OpenHuman emphasizes "behavior over implementation" and "no real network" for unit tests. Co-located Vitest tests should focus on what the UI does—rendered output, accessibility roles, and interaction—rather than internal component state or implementation details.

Use `vi.fn()` from Vitest to verify event handlers fire correctly, and query elements using `getByRole` to ensure tests validate accessibility. Assert on CSS class patterns when testing variant or size props, but avoid testing the exact internal structure of the component's DOM.

## Summary

- **Co-locate tests** by placing `*.test.tsx` files in the same directory as components (e.g., [`app/src/components/ui/Button.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/components/ui/Button.test.tsx) alongside [`Button.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/Button.tsx))
- **Configure Vitest** with the glob pattern `src/**/*.test.{ts,tsx}` in [`app/test/vitest.config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/test/vitest.config.ts) to automatically discover tests
- **Use relative imports** (`./ComponentName`) and Testing Library for maintainable, portable tests
- **Focus on behavioral assertions** (rendered output, accessibility roles, event handling) rather than implementation details
- **Reference global mocks** in [`app/src/test/setup.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/test/setup.ts) for consistent test environments across the suite

## Frequently Asked Questions

### What is the naming convention for co-located Vitest tests in OpenHuman?

Tests must use the pattern [`ComponentName.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/ComponentName.test.tsx) and reside in the same directory as the component or within a `__tests__` subdirectory. For example, [`SkillsExplorerTab.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/SkillsExplorerTab.tsx) is tested by [`__tests__/SkillsExplorerTab.test.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/__tests__/SkillsExplorerTab.test.tsx) in `app/src/components/skills/`.

### How does Vitest discover tests located in component directories?

The configuration file [`app/test/vitest.config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/test/vitest.config.ts) specifies the include pattern `src/**/*.test.{ts,tsx}`, which recursively searches the `src` directory for any files matching the test extension pattern, regardless of their specific folder depth.

### What testing library does OpenHuman use for component rendering?

OpenHuman uses `@testing-library/react` for rendering components and querying the DOM. Tests utilize `render` and `screen` methods to simulate user interactions and verify accessibility roles, following the "behavior over implementation" philosophy.

### Should tests focus on implementation details or user behavior?

Tests should focus on **user behavior** and rendered output rather than internal implementation. Query elements by accessible roles (e.g., `getByRole('button')`), assert on DOM attributes and CSS classes, and verify event handlers fire correctly, but avoid testing internal state or component structure directly.