Exemples Vitest

Exemples de framework de testing JavaScript moderne avec intégration Vite, featuring unit tests, component testing, et patterns de testing modernes

Key Facts

Category
Testing Frameworks
Items
4
Format Families
text

Sample Overview

Exemples de framework de testing JavaScript moderne avec intégration Vite, featuring unit tests, component testing, et patterns de testing modernes This sample set belongs to Testing Frameworks and can be used to test related workflows inside Elysia Tools.

💻 Configuration de Base Vitest text

🟢 simple ⭐⭐

Configuration complète de Vitest et configuration de testing de base avec support TypeScript

⏱️ 15 min 🏷️ vitest, testing, setup
Prerequisites: Node.js, npm/pnpm, Basic testing concepts
// Vitest Basic Setup Example

// 1. Installation
// npm install -D vitest jsdom @testing-library/vue @testing-library/react
// or: pnpm add -D vitest jsdom @testing-library/vue @testing-library/react

// 2. vitest.config.ts - Configuration file
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    vue(), // For Vue projects
    react(), // For React projects
  ],
  test: {
    // Global test environment
    environment: 'jsdom', // or 'node' for backend testing

    // Setup files to run before tests
    setupFiles: ['./src/test/setup.ts'],

    // Coverage configuration
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
        '**/*.config.*'
      ]
    },

    // Test matching patterns
    include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
    exclude: ['node_modules/', 'dist/', '.idea/', '.git/', '.cache/'],

    // Watch mode settings
    watch: false, // Set true for development

    // Global variables available in tests
    globals: true,

    // Timeout for each test (ms)
    testTimeout: 10000,

    // Hook timeout (ms)
    hookTimeout: 10000
  },

  // Regular Vite configuration
  resolve: {
    alias: {
      '@': '/src'
    }
  }
})

// 3. src/test/setup.ts - Test setup file
import { vi } from 'vitest'

// Mock global APIs
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: vi.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: vi.fn(), // deprecated
    removeListener: vi.fn(), // deprecated
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn(),
  })),
})

// Mock localStorage
const localStorageMock = {
  getItem: vi.fn(),
  setItem: vi.fn(),
  removeItem: vi.fn(),
  clear: vi.fn(),
}
vi.stubGlobal('localStorage', localStorageMock)

// Mock fetch
global.fetch = vi.fn()

// 4. Package.json scripts
const packageJson = {
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage",
    "test:watch": "vitest --watch"
  }
};

// 5. tsconfig.json - Add to compilerOptions
const tsconfig = {
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
};

💻 Testing Unitaire Vitest text

🟡 intermediate ⭐⭐⭐

Exemples complets de testing unitaire avec assertions, mocks, et testing asynchrone

⏱️ 25 min 🏷️ vitest, unit tests, mocks
Prerequisites: Basic Vitest setup, JavaScript/TypeScript, Testing concepts
// Vitest Unit Testing Examples

// 1. src/calculator.ts - Code to test
export class Calculator {
  add(a: number, b: number): number {
    return a + b
  }

  subtract(a: number, b: number): number {
    return a - b
  }

  multiply(a: number, b: number): number {
    return a * b
  }

  divide(a: number, b: number): number {
    if (b === 0) {
      throw new Error('Division by zero is not allowed')
    }
    return a / b
  }

  async asyncAdd(a: number, b: number): Promise<number> {
    return new Promise((resolve) => {
      setTimeout(() => resolve(a + b), 100)
    })
  }

  factorial(n: number): number {
    if (n < 0) throw new Error('Factorial of negative number')
    if (n === 0) return 1
    return n * this.factorial(n - 1)
  }
}

// 2. src/calculator.test.ts - Test file
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { Calculator } from './calculator'

describe('Calculator', () => {
  let calculator: Calculator

  beforeEach(() => {
    calculator = new Calculator()
  })

  afterEach(() => {
    vi.clearAllMocks()
  })

  describe('Basic arithmetic operations', () => {
    it('should add two numbers correctly', () => {
      expect(calculator.add(2, 3)).toBe(5)
      expect(calculator.add(-1, 1)).toBe(0)
      expect(calculator.add(0, 0)).toBe(0)
    })

    it('should subtract two numbers correctly', () => {
      expect(calculator.subtract(5, 3)).toBe(2)
      expect(calculator.subtract(0, 5)).toBe(-5)
      expect(calculator.subtract(-2, -3)).toBe(1)
    })

    it('should multiply two numbers correctly', () => {
      expect(calculator.multiply(3, 4)).toBe(12)
      expect(calculator.multiply(-2, 3)).toBe(-6)
      expect(calculator.multiply(0, 5)).toBe(0)
    })

    it('should divide two numbers correctly', () => {
      expect(calculator.divide(10, 2)).toBe(5)
      expect(calculator.divide(-6, 3)).toBe(-2)
      expect(calculator.divide(7, 2)).toBe(3.5)
    })

    it('should throw error when dividing by zero', () => {
      expect(() => calculator.divide(5, 0)).toThrow('Division by zero is not allowed')
    })
  })

  describe('Edge cases and special values', () => {
    it('should handle decimal numbers', () => {
      expect(calculator.add(2.5, 1.5)).toBeCloseTo(4.0)
      expect(calculator.multiply(2.5, 2)).toBe(5.0)
    })

    it('should handle very large numbers', () => {
      const largeNumber = Number.MAX_SAFE_INTEGER
      expect(calculator.add(largeNumber, 1)).toBe(largeNumber + 1)
    })
  })

  describe('Async operations', () => {
    it('should add numbers asynchronously', async () => {
      const result = await calculator.asyncAdd(2, 3)
      expect(result).toBe(5)
    })

    it('should handle async operations with timeout', async () => {
      const startTime = Date.now()
      await calculator.asyncAdd(1, 2)
      const endTime = Date.now()

      expect(endTime - startTime).toBeGreaterThanOrEqual(100)
    })
  })

  describe('Recursive operations', () => {
    it('should calculate factorial correctly', () => {
      expect(calculator.factorial(0)).toBe(1)
      expect(calculator.factorial(1)).toBe(1)
      expect(calculator.factorial(5)).toBe(120)
      expect(calculator.factorial(10)).toBe(3628800)
    })

    it('should throw error for negative factorial', () => {
      expect(() => calculator.factorial(-1)).toThrow('Factorial of negative number')
    })
  })
})

// 3. Advanced assertions examples
import { describe, it, expect } from 'vitest'

describe('Advanced Assertions', () => {
  it('demonstrates various matchers', () => {
    const user = {
      id: 1,
      name: 'John Doe',
      email: '[email protected]',
      preferences: {
        theme: 'dark',
        notifications: true
      },
      tags: ['developer', 'javascript']
    }

    // Basic equality
    expect(user.name).toBe('John Doe')

    // Object containing
    expect(user).toEqual(
      expect.objectContaining({
        name: 'John Doe',
        email: '[email protected]'
      })
    )

    // Array containing
    expect(user.tags).toEqual(
      expect.arrayContaining(['developer'])
    )

    // String matching
    expect(user.email).toMatch(/@example\.com$/)
    expect(user.email).toContain('@')

    // Number comparisons
    expect(user.id).toBeGreaterThan(0)
    expect(user.id).toBeLessThan(100)

    // Truthiness
    expect(user.preferences.notifications).toBeTruthy()
    expect(user.preferences.theme).toBeDefined()

    // Type checking
    expect(typeof user.name).toBe('string')
    expect(Array.isArray(user.tags)).toBe(true)

    // Custom matchers
    expect(user.tags.length).toBe(2)
  })

  it('demonstrates negated matchers', () => {
    const value = 'hello'

    expect(value).not.toBe('world')
    expect(value).not.toMatch(/[0-9]/)
    expect(value).not.toBeNull()
    expect(value).not.toBeUndefined()
  })
})

// 4. Mock functions examples
import { describe, it, expect, vi } from 'vitest'

describe('Mock Functions', () => {
  it('should create and track mock function calls', () => {
    const mockFn = vi.fn()

    mockFn()
    mockFn('hello')
    mockFn('world', 123)

    // Call counts
    expect(mockFn).toHaveBeenCalledTimes(3)

    // Call arguments
    expect(mockFn).toHaveBeenNthCalledWith(1)
    expect(mockFn).toHaveBeenNthCalledWith(2, 'hello')
    expect(mockFn).toHaveBeenNthCalledWith(3, 'world', 123)
  })

  it('should use mock return values', () => {
    const mockFn = vi.fn()
      .mockReturnValueOnce(42)
      .mockReturnValueOnce(100)
      .mockReturnValue('default')

    expect(mockFn()).toBe(42)
    expect(mockFn()).toBe(100)
    expect(mockFn()).toBe('default')
    expect(mockFn()).toBe('default')
  })

  it('should use mock resolved values', async () => {
    const asyncMock = vi.fn()
      .mockResolvedValueOnce('first result')
      .mockResolvedValueOnce('second result')
      .mockRejectedValueOnce(new Error('Async error'))

    expect(await asyncMock()).toBe('first result')
    expect(await asyncMock()).toBe('second result')
    await expect(asyncMock()).rejects.toThrow('Async error')
  })

  it('should mock implementation', () => {
    const addMock = vi.fn((a: number, b: number) => a + b)

    expect(addMock(2, 3)).toBe(5)
    expect(addMock).toHaveBeenCalledWith(2, 3)
    expect(addMock).toHaveBeenCalledTimes(1)
  })
})

💻 Testing de Composants Vitest text

🟡 intermediate ⭐⭐⭐

Exemples de testing de composants React et Vue avec Vitest et Testing Library

⏱️ 30 min 🏷️ vitest, component testing, react, vue
Prerequisites: React/Vue basics, Vitest setup, Testing Library
// Vitest Component Testing Examples

// 1. React Component Testing Example
// src/components/Counter.tsx
import React, { useState } from 'react'

interface CounterProps {
  initialValue?: number
  onCountChange?: (count: number) => void
}

export const Counter: React.FC<CounterProps> = ({
  initialValue = 0,
  onCountChange
}) => {
  const [count, setCount] = useState(initialValue)

  const increment = () => {
    const newCount = count + 1
    setCount(newCount)
    onCountChange?.(newCount)
  }

  const decrement = () => {
    const newCount = count - 1
    setCount(newCount)
    onCountChange?.(newCount)
  }

  const reset = () => {
    const newCount = initialValue
    setCount(newCount)
    onCountChange?.(newCount)
  }

  return (
    <div className="counter">
      <h2>Counter: {count}</h2>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>Reset</button>
    </div>
  )
}

// src/components/Counter.test.tsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { Counter } from './Counter'

describe('Counter Component', () => {
  it('renders with initial value', () => {
    render(<Counter initialValue={5} />)

    expect(screen.getByText('Counter: 5')).toBeInTheDocument()
    expect(screen.getByText('+')).toBeInTheDocument()
    expect(screen.getByText('-')).toBeInTheDocument()
    expect(screen.getByText('Reset')).toBeInTheDocument()
  })

  it('increments count when + button is clicked', () => {
    render(<Counter initialValue={0} />)

    const incrementButton = screen.getByText('+')
    fireEvent.click(incrementButton)

    expect(screen.getByText('Counter: 1')).toBeInTheDocument()
  })

  it('decrements count when - button is clicked', () => {
    render(<Counter initialValue={10} />)

    const decrementButton = screen.getByText('-')
    fireEvent.click(decrementButton)

    expect(screen.getByText('Counter: 9')).toBeInTheDocument()
  })

  it('resets count when reset button is clicked', () => {
    render(<Counter initialValue={5} />)

    // First increment
    fireEvent.click(screen.getByText('+'))
    expect(screen.getByText('Counter: 6')).toBeInTheDocument()

    // Then reset
    fireEvent.click(screen.getByText('Reset'))
    expect(screen.getByText('Counter: 5')).toBeInTheDocument()
  })

  it('calls onCountChange callback when count changes', () => {
    const mockOnChange = vi.fn()
    render(<Counter initialValue={0} onCountChange={mockOnChange} />)

    fireEvent.click(screen.getByText('+'))
    expect(mockOnChange).toHaveBeenCalledWith(1)

    fireEvent.click(screen.getByText('-'))
    expect(mockOnChange).toHaveBeenCalledWith(-1)

    fireEvent.click(screen.getByText('Reset'))
    expect(mockOnChange).toHaveBeenCalledWith(0)
  })
})

// 2. Vue Component Testing Example
// src/components/UserProfile.vue
<template>
  <div class="user-profile">
    <div v-if="loading" class="loading">Loading...</div>
    <div v-else-if="error" class="error">{{ error }}</div>
    <div v-else class="profile">
      <img :src="user.avatar" :alt="user.name" class="avatar" />
      <h2>{{ user.name }}</h2>
      <p>{{ user.email }}</p>
      <div class="stats">
        <span>Followers: {{ user.followers }}</span>
        <span>Following: {{ user.following }}</span>
      </div>
      <button @click="toggleFollow">
        {{ isFollowing ? 'Unfollow' : 'Follow' }}
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

interface User {
  id: number
  name: string
  email: string
  avatar: string
  followers: number
  following: number
}

interface Props {
  userId: number
}

const props = defineProps<Props>()
const emit = defineEmits<{
  follow: [{ userId: number, isFollowing: boolean }]
}>()

const loading = ref(true)
const error = ref<string | null>(null)
const user = ref<User | null>(null)
const isFollowing = ref(false)

const followCount = computed(() => {
  return user.value ? user.value.followers + (isFollowing.value ? 1 : 0) : 0
})

const loadUser = async () => {
  try {
    loading.value = true
    error.value = null

    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 1000))

    // Mock data
    user.value = {
      id: props.userId,
      name: `User ${props.userId}`,
      email: `user${props.userId}@example.com`,
      avatar: `https://i.pravatar.cc/150?img=${props.userId}`,
      followers: 100,
      following: 50
    }
  } catch (err) {
    error.value = err instanceof Error ? err.message : 'Failed to load user'
  } finally {
    loading.value = false
  }
}

const toggleFollow = () => {
  if (user.value) {
    isFollowing.value = !isFollowing.value
    emit('follow', {
      userId: user.value.id,
      isFollowing: isFollowing.value
    })
  }
}

loadUser()
</script>

// src/components/UserProfile.test.ts
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import UserProfile from './UserProfile.vue'

describe('UserProfile Component', () => {
  it('shows loading state initially', () => {
    const wrapper = mount(UserProfile, {
      props: { userId: 1 }
    })

    expect(wrapper.find('.loading').exists()).toBe(true)
    expect(wrapper.text()).toContain('Loading...')
  })

  it('displays user profile after loading', async () => {
    const wrapper = mount(UserProfile, {
      props: { userId: 1 }
    })

    // Wait for async data loading
    await new Promise(resolve => setTimeout(resolve, 1100))
    await nextTick()

    expect(wrapper.find('.loading').exists()).toBe(false)
    expect(wrapper.find('.profile').exists()).toBe(true)
    expect(wrapper.text()).toContain('User 1')
    expect(wrapper.text()).toContain('[email protected]')
  })

  it('shows follow button and handles clicks', async () => {
    const wrapper = mount(UserProfile, {
      props: { userId: 1 }
    })

    await new Promise(resolve => setTimeout(resolve, 1100))
    await nextTick()

    const followButton = wrapper.find('button')
    expect(followButton.exists()).toBe(true)
    expect(followButton.text()).toBe('Follow')

    await followButton.trigger('click')
    expect(followButton.text()).toBe('Unfollow')

    const emitted = wrapper.emitted('follow')
    expect(emitted).toBeDefined()
    expect(emitted![0]).toEqual([{ userId: 1, isFollowing: true }])
  })

  it('calculates follower count correctly', async () => {
    const wrapper = mount(UserProfile, {
      props: { userId: 1 }
    })

    await new Promise(resolve => setTimeout(resolve, 1100))
    await nextTick()

    expect(wrapper.text()).toContain('Followers: 100')

    await wrapper.find('button').trigger('click')
    await nextTick()

    expect(wrapper.text()).toContain('Followers: 101')
  })
})

// 3. Custom Hooks Testing Example
// src/hooks/useCounter.ts
import { ref, computed } from 'vue'

export function useCounter(initialValue: number = 0) {
  const count = ref(initialValue)

  const increment = () => {
    count.value += 1
  }

  const decrement = () => {
    count.value -= 1
  }

  const reset = () => {
    count.value = initialValue
  }

  const setValue = (value: number) => {
    count.value = value
  }

  const isEven = computed(() => count.value % 2 === 0)
  const isPositive = computed(() => count.value > 0)

  return {
    count: readonly(count),
    increment,
    decrement,
    reset,
    setValue,
    isEven,
    isPositive
  }
}

// src/hooks/useCounter.test.ts
import { describe, it, expect } from 'vitest'
import { useCounter } from './useCounter'
import { ref } from 'vue'

describe('useCounter Hook', () => {
  it('initializes with default value', () => {
    const { count, isEven, isPositive } = useCounter()

    expect(count.value).toBe(0)
    expect(isEven.value).toBe(true)
    expect(isPositive.value).toBe(false)
  })

  it('initializes with custom value', () => {
    const { count } = useCounter(5)

    expect(count.value).toBe(5)
  })

  it('increments count correctly', () => {
    const { count, increment } = useCounter(2)

    increment()
    expect(count.value).toBe(3)

    increment()
    expect(count.value).toBe(4)
  })

  it('decrements count correctly', () => {
    const { count, decrement } = useCounter(5)

    decrement()
    expect(count.value).toBe(4)

    decrement()
    expect(count.value).toBe(3)
  })

  it('resets count to initial value', () => {
    const { count, increment, reset } = useCounter(10)

    increment()
    increment()
    expect(count.value).toBe(12)

    reset()
    expect(count.value).toBe(10)
  })

  it('sets count to specific value', () => {
    const { count, setValue } = useCounter()

    setValue(100)
    expect(count.value).toBe(100)
  })

  it('computes even/odd correctly', () => {
    const { count, increment, isEven } = useCounter(0)

    expect(isEven.value).toBe(true)

    increment()
    expect(isEven.value).toBe(false)

    increment()
    expect(isEven.value).toBe(true)
  })
})

💻 Patterns de Testing Avancés Vitest text

🔴 complex ⭐⭐⭐⭐

Patterns de testing avancés incluant integration tests, performance tests, et utilitaires de testing

⏱️ 40 min 🏷️ vitest, integration, performance, advanced
Prerequisites: Advanced Vitest, TypeScript, Testing patterns
// Vitest Advanced Testing Patterns

// 1. Integration Testing Example
// src/services/userService.ts
export interface User {
  id: number
  name: string
  email: string
  role: 'user' | 'admin'
}

export interface UserRepository {
  findById(id: number): Promise<User | null>
  create(userData: Omit<User, 'id'>): Promise<User>
  update(id: number, userData: Partial<User>): Promise<User>
  delete(id: number): Promise<boolean>
}

export class UserService {
  constructor(private repository: UserRepository) {}

  async getUser(id: number): Promise<User | null> {
    const user = await this.repository.findById(id)
    if (!user) {
      throw new Error(`User with id ${id} not found`)
    }
    return user
  }

  async createUser(userData: Omit<User, 'id'>): Promise<User> {
    if (!userData.email?.includes('@')) {
      throw new Error('Invalid email format')
    }
    return this.repository.create(userData)
  }

  async updateUserRole(id: number, role: User['role']): Promise<User> {
    const user = await this.getUser(id)
    return this.repository.update(id, { role })
  }

  async deleteUser(id: number): Promise<boolean> {
    await this.getUser(id) // Will throw if user doesn't exist
    return this.repository.delete(id)
  }
}

// src/services/userService.integration.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { UserService, User, UserRepository } from './userService'

// Mock repository for testing
class MockUserRepository implements UserRepository {
  private users: User[] = []
  private nextId = 1

  async findById(id: number): Promise<User | null> {
    return this.users.find(user => user.id === id) || null
  }

  async create(userData: Omit<User, 'id'>): Promise<User> {
    const user: User = {
      id: this.nextId++,
      ...userData
    }
    this.users.push(user)
    return user
  }

  async update(id: number, userData: Partial<User>): Promise<User> {
    const userIndex = this.users.findIndex(user => user.id === id)
    if (userIndex === -1) {
      throw new Error(`User with id ${id} not found`)
    }
    this.users[userIndex] = { ...this.users[userIndex], ...userData }
    return this.users[userIndex]
  }

  async delete(id: number): Promise<boolean> {
    const userIndex = this.users.findIndex(user => user.id === id)
    if (userIndex === -1) return false
    this.users.splice(userIndex, 1)
    return true
  }

  clear() {
    this.users = []
    this.nextId = 1
  }
}

describe('UserService Integration Tests', () => {
  let userService: UserService
  let mockRepository: MockUserRepository

  beforeEach(() => {
    mockRepository = new MockUserRepository()
    userService = new UserService(mockRepository)
    mockRepository.clear()
  })

  describe('User CRUD operations', () => {
    it('should create and retrieve a user', async () => {
      const userData = {
        name: 'John Doe',
        email: '[email protected]',
        role: 'user' as const
      }

      const createdUser = await userService.createUser(userData)
      expect(createdUser.id).toBe(1)
      expect(createdUser.name).toBe('John Doe')

      const retrievedUser = await userService.getUser(createdUser.id)
      expect(retrievedUser).toEqual(createdUser)
    })

    it('should throw error for invalid email when creating user', async () => {
      const userData = {
        name: 'John Doe',
        email: 'invalid-email',
        role: 'user' as const
      }

      await expect(userService.createUser(userData)).rejects.toThrow('Invalid email format')
    })

    it('should update user role', async () => {
      const userData = {
        name: 'John Doe',
        email: '[email protected]',
        role: 'user' as const
      }

      const user = await userService.createUser(userData)
      const updatedUser = await userService.updateUserRole(user.id, 'admin')

      expect(updatedUser.role).toBe('admin')
    })

    it('should delete user', async () => {
      const userData = {
        name: 'John Doe',
        email: '[email protected]',
        role: 'user' as const
      }

      const user = await userService.createUser(userData)
      const deleted = await userService.deleteUser(user.id)

      expect(deleted).toBe(true)
      await expect(userService.getUser(user.id)).rejects.toThrow('not found')
    })
  })
})

// 2. Performance Testing Example
// src/utils/performance.ts
export function fibonacci(n: number): number {
  if (n <= 1) return n
  return fibonacci(n - 1) + fibonacci(n - 2)
}

export function memoizedFibonacci(): (n: number) => number {
  const cache = new Map<number, number>()

  return function fib(n: number): number {
    if (cache.has(n)) return cache.get(n)!
    if (n <= 1) return n

    const result = fib(n - 1) + fib(n - 2)
    cache.set(n, result)
    return result
  }
}

export async function processLargeArray<T>(array: T[], processor: (item: T) => Promise<T>): Promise<T[]> {
  const promises = array.map(processor)
  return Promise.all(promises)
}

// src/utils/performance.test.ts
import { describe, it, expect } from 'vitest'
import { fibonacci, memoizedFibonacci, processLargeArray } from './performance'

describe('Performance Tests', () => {
  it('should complete fibonacci calculation within reasonable time', () => {
    const start = performance.now()
    const result = fibonacci(20)
    const end = performance.now()

    expect(result).toBe(6765)
    expect(end - start).toBeLessThan(1000) // Should complete within 1 second
  })

  it('should show memoized version is significantly faster', () => {
    const memFib = memoizedFibonacci()
    const n = 40

    // Non-memoized version
    const start1 = performance.now()
    const result1 = fibonacci(n)
    const end1 = performance.now()
    const time1 = end1 - start1

    // Memoized version
    const start2 = performance.now()
    const result2 = memFib(n)
    const end2 = performance.now()
    const time2 = end2 - start2

    expect(result1).toBe(result2)
    expect(time2).toBeLessThan(time1 / 10) // Memoized should be at least 10x faster
  })

  it('should process large array efficiently', async () => {
    const largeArray = Array.from({ length: 1000 }, (_, i) => i)

    const processor = async (item: number): Promise<number> => {
      await new Promise(resolve => setTimeout(resolve, 1))
      return item * 2
    }

    const start = performance.now()
    const result = await processLargeArray(largeArray, processor)
    const end = performance.now()

    expect(result).toHaveLength(1000)
    expect(result[0]).toBe(0)
    expect(result[999]).toBe(1998)
    expect(end - start).toBeLessThan(5000) // Should complete within 5 seconds
  })
})

// 3. Test Utilities and Fixtures
// src/test/utils/testUtils.ts
import { render, RenderOptions } from '@testing-library/react'
import { ReactElement } from 'react'

// Custom render function with providers
export const renderWithProviders = (
  ui: ReactElement,
  options: RenderOptions = {}
) => {
  // Add any global providers here (Theme, Router, etc.)
  return render(ui, {
    ...options,
  })
}

// Test data factory
export class UserFactory {
  static create(overrides: Partial<User> = {}): User {
    return {
      id: Math.floor(Math.random() * 1000),
      name: 'Test User',
      email: '[email protected]',
      role: 'user',
      ...overrides
    }
  }

  static createMany(count: number, overrides: Partial<User> = {}): User[] {
    return Array.from({ length: count }, () => this.create(overrides))
  }
}

// Mock responses
export const createMockApiResponse = <T>(data: T, status = 200) => ({
  data,
  status,
  headers: { 'content-type': 'application/json' }
})

// 4. API Testing Example
// src/services/api.ts
export interface ApiResponse<T> {
  data: T
  status: number
  message: string
}

export class ApiClient {
  constructor(private baseUrl: string) {}

  async get<T>(endpoint: string): Promise<ApiResponse<T>> {
    const response = await fetch(`${this.baseUrl}${endpoint}`)
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    const data = await response.json()
    return {
      data,
      status: response.status,
      message: 'Success'
    }
  }

  async post<T>(endpoint: string, body: any): Promise<ApiResponse<T>> {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body)
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const data = await response.json()
    return {
      data,
      status: response.status,
      message: 'Created'
    }
  }
}

// src/services/api.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { ApiClient } from './api'

describe('ApiClient', () => {
  let apiClient: ApiClient
  let mockFetch: ReturnType<typeof vi.fn>

  beforeEach(() => {
    mockFetch = vi.fn()
    global.fetch = mockFetch
    apiClient = new ApiClient('https://api.example.com')
  })

  it('should make GET request and return response', async () => {
    const mockData = { id: 1, name: 'Test' }
    mockFetch.mockResolvedValueOnce({
      ok: true,
      status: 200,
      json: async () => mockData
    })

    const result = await apiClient.get('/users/1')

    expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/users/1')
    expect(result.data).toEqual(mockData)
    expect(result.status).toBe(200)
    expect(result.message).toBe('Success')
  })

  it('should handle HTTP errors', async () => {
    mockFetch.mockResolvedValueOnce({
      ok: false,
      status: 404
    })

    await expect(apiClient.get('/users/999')).rejects.toThrow('HTTP error! status: 404')
  })

  it('should make POST request with body', async () => {
    const requestBody = { name: 'New User' }
    const responseData = { id: 2, ...requestBody }

    mockFetch.mockResolvedValueOnce({
      ok: true,
      status: 201,
      json: async () => responseData
    })

    const result = await apiClient.post('/users', requestBody)

    expect(mockFetch).toHaveBeenCalledWith(
      'https://api.example.com/users',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(requestBody)
      }
    )
    expect(result.data).toEqual(responseData)
    expect(result.status).toBe(201)
    expect(result.message).toBe('Created')
  })
})