Функция append в Go Golang

19 февраля 2026
Автор

Олег Марков

Введение

Тестирование компонентов — это подход, при котором вы проверяете отдельные, относительно небольшие части системы в изоляции от остального приложения. Эти части могут быть UI-компонентами (React, Vue, Angular), серверными модулями, классами или функциями, которые несут законченную часть поведения.

Смотрите, логика здесь простая: чем раньше вы находите ошибку и чем ближе вы находитесь к месту, где она возникла, тем дешевле и проще её исправить. Компонентное тестирование как раз и нацелено на то, чтобы локализовать проверки и держать их максимально близко к коду.

В этой статье мы пройдём путь от базовой теории до вполне практичных примеров. Я буду больше опираться на примеры на JavaScript/TypeScript и React, потому что там термин «компонент» используется чаще всего, но принципы, о которых мы поговорим, хорошо переносятся и на бэкенд, и на другие технологии.

Что такое тестирование компонентов и чем оно отличается от других видов тестов

Компонент как единица тестирования

С точки зрения тестирования, компонент — это модуль, который:

  • Имеет чётко определённый интерфейс (пропсы, параметры, публичные методы).
  • Содержит внутреннюю реализацию (состояние, побочные эффекты, внутренние функции).
  • Может быть запущен в изоляции.

Для UI это, например, кнопка, форма, виджет фильтрации. Для серверного кода — сервис авторизации, класс для расчёта скидки, адаптер к внешнему API.

Тестируя компонент, вы проверяете, что:

  • На одни и те же входные данные он даёт одни и те же выходы.
  • Корректно вызывает зависимости (если они есть).
  • Ведёт себя одинаково предсказуемо вне зависимости от окружения.

Сравнение с unit, integration и e2e

Чтобы не путаться в терминах, давайте коротко разведём виды тестов:

  • Модульные (unit) — проверяют отдельную функцию/метод, обычно вообще без реальных зависимостей, всё замокано.
  • Компонентные — проверяют часть системы чуть крупнее: UI-компонент, класс, сервис. Часто допускают использование реальных зависимостей, если они локальные и недорогие.
  • Интеграционные — проверяют взаимодействие нескольких модулей (например, сервис + БД, компонент + API).
  • End-to-end (e2e) — проверяют систему целиком «как пользователь», через интерфейс или публичные endpoints.

Компонентные тесты чаще всего стоят между unit и интеграционными: вы не доходите до всей системы, но и не сводите всё к чистым функциям.

Зачем вам тестирование компонентов

Основные цели

Тестирование компонентов помогает:

  • Уверенно рефакторить код, не боясь сломать поведение.
  • Поймать регрессии (когда что-то старое ломается из‑за новых изменений).
  • Документировать поведение компонента через примеры.
  • Уменьшить количество ошибок, которые доходят до интеграционных и e2e тестов.

Особенно полезно это для UI, где много состояний, условий отображения и возможных сценариев.

Что именно имеет смысл тестировать в компоненте

Обычно полезно проверять:

  • Рендеринг — что именно отображается при разных входных данных.
  • Состояние — как меняется state/данные при действиях пользователя или приходе новых пропсов.
  • Взаимодействия — клики, ввод текста, отправка формы.
  • Взаимодействие с зависимостями — правильно ли вызываются коллбеки, API, хранилище.

То, что обычно не стоит тестировать напрямую:

  • Внутренние детали реализации, которые легко поменять (имена приватных функций, структура DOM-глубины, если она не является частью контракта).
  • Конкретные реализации стилей, если они не влияют на поведение (цвета, отступы).

Инструменты для тестирования компонентов на примере React

Чтобы не говорить абстрактно, давайте возьмём связку Jest + React Testing Library (RTL). Подходы, про которые я рассказываю, можно перенести и на другие фреймворки.

Базовая настройка окружения

Предположим, у вас есть React-приложение. Для тестирования компонентов нам обычно нужны:

  • Jest — тестовый раннер.
  • React Testing Library — для рендера и взаимодействий.
  • Jest DOM matcher'ы — для удобных проверок.

Пример установки:

npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event

Далее вы настраиваете Jest-конфигурацию (в простых случаях она уже есть в create-react-app или Vite шаблонах).

Подключение jest-dom, чтобы иметь дополнительные матчеры:

// setupTests.js
import '@testing-library/jest-dom'

// В этом файле вы расширяете Jest дополнительными возможностями // Теперь можно использовать expect(element).toBeInTheDocument() и другие удобные проверки

Теперь Jest нужно сказать, что этот файл setupTests.js должен выполняться перед тестами (в конфигурации jest, например, через setupFilesAfterEnv).

Первый компонент: кнопка с коллбеком

Давайте разберём простой компонент, чтобы показать базовые приёмы.

// Button.tsx
import React from 'react'

type ButtonProps = {
  label: string
  disabled?: boolean
  onClick?: () => void
}

export const Button = ({ label, disabled = false, onClick }: ButtonProps) => {
  return (
    <button
      type="button"
      disabled={disabled}
      onClick={onClick}
    >
      {label}
    </button>
  )
}

// Здесь мы описываем простой компонент кнопки // - label - текст на кнопке // - disabled - флаг, блокирующий кнопку // - onClick - обработчик клика

Теперь давайте напишем к нему компонентный тест.

// Button.test.tsx
import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from './Button'

test('рендерит текст переданный в label', () => {
  // Рендерим компонент с конкретным текстом
  render(<Button label="Сохранить" />)

  // Ищем элемент по тексту
  const button = screen.getByRole('button', { name: 'Сохранить' })

  // Проверяем, что он действительно есть в документе
  expect(button).toBeInTheDocument()
})

test('вызывает onClick при клике если не disabled', async () => {
  const user = userEvent.setup()
  const handleClick = jest.fn()

  render(<Button label="Удалить" onClick={handleClick} />)

  const button = screen.getByRole('button', { name: 'Удалить' })

  // Выполняем клик по кнопке
  await user.click(button)

  // Проверяем, что обработчик был вызван один раз
  expect(handleClick).toHaveBeenCalledTimes(1)
})

test('не вызывает onClick если disabled', async () => {
  const user = userEvent.setup()
  const handleClick = jest.fn()

  render(<Button label="Удалить" onClick={handleClick} disabled />)

  const button = screen.getByRole('button', { name: 'Удалить' })

  // Пробуем кликнуть по заблокированной кнопке
  await user.click(button)

  // Убеждаемся, что обработчик не вызвался
  expect(handleClick).not.toHaveBeenCalled()
})

Обратите внимание, что мы ведём себя так же, как пользователь: ищем кнопку по роли и тексту, кликаем по ней, а затем проверяем эффект.

Принципы хороших компонентных тестов

Тестируйте поведение, а не реализацию

Очень распространённая ошибка — привязываться к внутренней структуре компонента. Например, проверять конкретные имена CSS-классов, количество вложенных div, вызовы внутренних функций.

Гораздо надёжнее проверять поведение:

  • Что видит пользователь.
  • Какие внешние эффекты происходят (вызов пропса, отправка запроса).

Пример плохого теста:

// Плохой пример - тест жёстко привязан к структуре DOM
const element = container.querySelector('div > span > button')
expect(element?.className).toBe('btn btn-primary')

// Такой тест легко сломается при безобидном рефакторинге верстки

Лучше искать элементы так, как их нашёл бы пользователь:

  • По тексту.
  • По ролям.
  • По aria-атрибутам.
  • По placeholder/value (для полей ввода).
// Хороший пример - ищем элемент по роли и имени
const button = screen.getByRole('button', { name: /сохранить/i })
expect(button).toBeEnabled()

Один тест — один сценарий поведения

Полезно придерживаться структуры:

  • Given (условия).
  • When (действие).
  • Then (ожидания).

Смотрите, как это выглядит на практике:

test('отправляет форму при валидных данных', async () => {
  const user = userEvent.setup()
  const handleSubmit = jest.fn()

  // Given - рендерим форму с обработчиком отправки
  render(<LoginForm onSubmit={handleSubmit} />)

  // When - заполняем поля и нажимаем кнопку
  await user.type(screen.getByLabelText(/email/i), 'user@example.com')
  await user.type(screen.getByLabelText(/password/i), 'secret')
  await user.click(screen.getByRole('button', { name: /войти/i }))

  // Then - проверяем, что данные ушли в обработчик
  expect(handleSubmit).toHaveBeenCalledWith({
    email: 'user@example.com',
    password: 'secret',
  })
})

// Здесь мы чётко видим условия, действие и ожидаемый результат // Такой тест легко читать и поддерживать

Держите тесты независимыми

Каждый тест компонентного поведения должен:

  • Самостоятельно подготавливать необходимые данные/рендер.
  • Не зависеть от выполнения других тестов.
  • Самостоятельно очищать за собой (обычно библиотеки делают это автоматически).

Не стоит полагаться на то, что предыдущий тест уже отрендерил компонент в нужном состоянии.

Тестирование состояния и логики внутри компонента

Компонент со внутренним состоянием

Теперь давайте возьмём компонент посложнее — счётчик с локальным состоянием.

// Counter.tsx
import React, { useState } from 'react'

type CounterProps = {
  initial?: number
  step?: number
}

export const Counter = ({ initial = 0, step = 1 }: CounterProps) => {
  const [value, setValue] = useState(initial)

  const handleIncrement = () => {
    // Здесь мы обновляем состояние, добавляя шаг
    setValue((prev) => prev + step)
  }

  const handleReset = () => {
    // Здесь мы сбрасываем состояние к начальному значению
    setValue(initial)
  }

  return (
    <div>
      <span data-testid="counter-value">{value}</span>
      <button onClick={handleIncrement}>Плюс</button>
      <button onClick={handleReset}>Сброс</button>
    </div>
  )
}

Теперь вы увидите, как это выглядит в коде теста:

// Counter.test.tsx
import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Counter } from './Counter'

test('показывает начальное значение', () => {
  render(<Counter initial={5} />)

  // Ищем элемент по data-testid
  const value = screen.getByTestId('counter-value')

  // Проверяем текстовое содержимое
  expect(value).toHaveTextContent('5')
})

test('увеличивает значение на шаг при клике', async () => {
  const user = userEvent.setup()
  render(<Counter initial={0} step={2} />)

  const value = screen.getByTestId('counter-value')
  const incButton = screen.getByRole('button', { name: 'Плюс' })

  await user.click(incButton)
  expect(value).toHaveTextContent('2')

  await user.click(incButton)
  expect(value).toHaveTextContent('4')
})

test('сбрасывает значение к начальному', async () => {
  const user = userEvent.setup()
  render(<Counter initial={10} />)

  const value = screen.getByTestId('counter-value')
  const incButton = screen.getByRole('button', { name: 'Плюс' })
  const resetButton = screen.getByRole('button', { name: 'Сброс' })

  // Увеличиваем значение
  await user.click(incButton)
  await user.click(incButton)
  expect(value).toHaveTextContent('12')

  // Сбрасываем
  await user.click(resetButton)
  expect(value).toHaveTextContent('10')
})

Обратите внимание, что тесты не залезают внутрь useState и не проверяют, как именно он устроен. Мы проверяем только видимое поведение.

Когда тестировать «чистую» логику отдельно

Если логика компонента становится сложной (много условий, вычислений), есть смысл вынести её в отдельные чистые функции или кастомные хуки.

Например:

// discount.ts
// Эта функция считает скидку, не зная ничего о компоненте
export const calculateDiscount = (price: number, percent: number): number => {
  if (percent < 0 || percent > 100) {
    throw new Error('invalid percent')
  }

  return price - (price * percent) / 100
}

Такую функцию удобно тестировать юнит-тестами отдельно от компонента, а в компоненте уже проверить только то, что результат правильно отображается.

// discount.test.ts
import { calculateDiscount } from './discount'

test('корректно считает скидку', () => {
  expect(calculateDiscount(100, 10)).toBe(90)
  expect(calculateDiscount(200, 50)).toBe(100)
})

test('выбрасывает ошибку при некорректном проценте', () => {
  expect(() => calculateDiscount(100, -5)).toThrow('invalid percent')
  expect(() => calculateDiscount(100, 150)).toThrow('invalid percent')
})

// Здесь мы тестируем только математику и правила // Компоненты, которые используют эту функцию, не обязаны знать её внутренние детали

Работа с зависимостями компонентов

Компоненты редко живут в вакууме. Чаще всего они:

  • Получают данные из API.
  • Общаются с глобальными сторами (Redux, Zustand, Vuex).
  • Используют контексты.

Ввод через пропсы vs глобальные зависимости

С точки зрения тестирования, ввод через пропсы гораздо удобнее, чем жёсткие глобальные зависимости. Посмотрите на два подхода.

Неудобный вариант:

// Плохой подход - внутри компонента мы напрямую обращаемся к глобальному объекту
import { api } from '../api'

export const Profile = () => {
  // Здесь компонент сам решает, какие данные получать
  // Для теста это усложняет подмену поведения
}

Гораздо проще тестировать, когда зависимости приходят снаружи:

// Хороший подход - зависимости приходят как пропсы
type ProfileProps = {
  loadUser: () => Promise<User>
}

export const Profile = ({ loadUser }: ProfileProps) => {
  // Теперь loadUser можно легко подменить в тестах
}

В тесте вы можете передать фейковую функцию:

const fakeUser = { id: 1, name: 'Alice' }

const loadUser = jest.fn().mockResolvedValue(fakeUser)

render(<Profile loadUser={loadUser} />)

// В таком варианте вы полностью контролируете поведение зависимостей // Без сложного мокинга модулей и глобальных объектов

Тестирование взаимодействия с API через моки

Если у вас всё-таки есть модуль api, который вызывается внутри компонента, можно замокать его с помощью Jest.

// api.ts
export const api = {
  async loadUser() {
    // Здесь реальный запрос
  },
}

Мок в тесте:

// Profile.test.tsx
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import { Profile } from './Profile'

// Импортируем модуль, который будем мокать
import { api } from '../api'

// Говорим Jest, что хотим мокать этот модуль
jest.mock('../api', () => ({
  api: {
    loadUser: jest.fn(),
  },
}))

test('показывает имя пользователя после загрузки', async () => {
  // Настраиваем поведение замоканной функции
  ;(api.loadUser as jest.Mock).mockResolvedValue({
    id: 1,
    name: 'Alice',
  })

  render(<Profile />)

  // Здесь мы ждём появления имени на экране
  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument()
  })
})

// Мы подменили реальный вызов API фейковой функцией // Теперь компонент тестируется быстро и без реальных сетевых запросов

Тестирование компонентов, зависящих от контекста

Когда компонент использует React Context, в тестах нужно «обернуть» его в соответствующий провайдер.

Пример:

// AuthContext.tsx
import React, { createContext, useContext } from 'react'

type AuthContextValue = {
  user: { name: string } | null
}

const AuthContext = createContext<AuthContextValue>({ user: null })

export const useAuth = () => useContext(AuthContext)

export const AuthProvider = ({
  value,
  children,
}: {
  value: AuthContextValue
  children: React.ReactNode
}) => {
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

Компонент:

// Greeting.tsx
import React from 'react'
import { useAuth } from './AuthContext'

export const Greeting = () => {
  const { user } = useAuth()

  if (!user) {
    return <span>Гость</span>
  }

  return <span>Привет, {user.name}</span>
}

Тест:

// Greeting.test.tsx
import React from 'react'
import { render, screen } from '@testing-library/react'
import { Greeting } from './Greeting'
import { AuthProvider } from './AuthContext'

const renderWithAuth = (ui: React.ReactElement, { user = null } = {}) => {
  // Оборачиваем компонент в провайдер контекста
  return render(
    <AuthProvider value={{ user }}>
      {ui}
    </AuthProvider>
  )
}

test('показывает "Гость" если пользователь не авторизован', () => {
  renderWithAuth(<Greeting />)

  expect(screen.getByText('Гость')).toBeInTheDocument()
})

test('показывает имя пользователя если он авторизован', () => {
  renderWithAuth(<Greeting />, { user: { name: 'Alice' } })

  expect(screen.getByText('Привет, Alice')).toBeInTheDocument()
})

// Мы создали вспомогательную функцию renderWithAuth // Она упрощает рендер компонентов с нужным контекстом в разных тестах

Тестирование сложных взаимодействий и асинхронности

Поле ввода с валидацией

Давайте посмотрим на компонент формы с простой валидацией.

// EmailInput.tsx
import React, { useState } from 'react'

type EmailInputProps = {
  onValidEmail: (email: string) => void
}

const isEmailValid = (value: string) => {
  // Упрощенная проверка email для примера
  return value.includes('@')
}

export const EmailInput = ({ onValidEmail }: EmailInputProps) => {
  const [value, setValue] = useState('')
  const [error, setError] = useState<string | null>(null)

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = e.target.value
    setValue(newValue)

    if (!isEmailValid(newValue)) {
      // Если email некорректный, показываем ошибку
      setError('Некорректный email')
    } else {
      // Если корректный, скрываем ошибку и уведомляем родителя
      setError(null)
      onValidEmail(newValue)
    }
  }

  return (
    <div>
      <label>
        Email
        <input
          type="email"
          value={value}
          onChange={handleChange}
          aria-invalid={!!error}
        />
      </label>
      {error && <span role="alert">{error}</span>}
    </div>
  )
}

Тестируем два сценария: неправильный ввод и корректный.

// EmailInput.test.tsx
import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { EmailInput } from './EmailInput'

test('показывает ошибку при некорректном email', async () => {
  const user = userEvent.setup()
  const handleValidEmail = jest.fn()

  render(<EmailInput onValidEmail={handleValidEmail} />)

  const input = screen.getByLabelText(/email/i)

  // Вводим заведомо некорректный email
  await user.type(input, 'invalid-email')

  // Ожидаем увидеть сообщение об ошибке
  expect(screen.getByRole('alert')).toHaveTextContent('Некорректный email')

  // И проверяем, что валидный обработчик не вызывался
  expect(handleValidEmail).not.toHaveBeenCalled()
})

test('вызывает onValidEmail при корректном email и скрывает ошибку', async () => {
  const user = userEvent.setup()
  const handleValidEmail = jest.fn()

  render(<EmailInput onValidEmail={handleValidEmail} />)

  const input = screen.getByLabelText(/email/i)

  // Вводим последовательно некорректный, а затем корректный email
  await user.type(input, 'invalid-email')
  await user.clear(input)
  await user.type(input, 'user@example.com')

  // Ошибка должна исчезнуть
  expect(screen.queryByRole('alert')).toBeNull()

  // И обработчик должен быть вызван с корректным значением
  expect(handleValidEmail).toHaveBeenCalledWith('user@example.com')
})

Асинхронные операции в компонентах

Когда компонент делает асинхронный запрос (например, в useEffect), тесты должны уметь дождаться завершения операции.

Пример компонента:

// UserInfo.tsx
import React, { useEffect, useState } from 'react'
import { api } from '../api'

type User = {
  id: number
  name: string
}

export const UserInfo = ({ userId }: { userId: number }) => {
  const [user, setUser] = useState<User | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setError(null)

    api
      .loadUser(userId)
      .then((data) => {
        if (!cancelled) {
          setUser(data)
        }
      })
      .catch(() => {
        if (!cancelled) {
          setError('Ошибка загрузки')
        }
      })
      .finally(() => {
        if (!cancelled) {
          setLoading(false)
        }
      })

    return () => {
      // Флаг на случай, если компонент размонтируется до завершения запроса
      cancelled = true
    }
  }, [userId])

  if (loading) {
    return <span>Загрузка...</span>
  }

  if (error) {
    return <span role="alert">{error}</span>
  }

  if (!user) {
    return null
  }

  return <div>Пользователь {user.name}</div>
}

Теперь давайте посмотрим, что происходит в тесте:

// UserInfo.test.tsx
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import { UserInfo } from './UserInfo'
import { api } from '../api'

jest.mock('../api', () => ({
  api: {
    loadUser: jest.fn(),
  },
}))

test('показывает индикатор загрузки пока данные не получены', async () => {
  // Создаем промис, который сможем контролировать вручную
  let resolvePromise: (value: any) => void

  const loadUserPromise = new Promise((resolve) => {
    resolvePromise = resolve
  })

  ;(api.loadUser as jest.Mock).mockReturnValue(loadUserPromise)

  render(<UserInfo userId={1} />)

  // Сразу после рендера должен отображаться текст "Загрузка..."
  expect(screen.getByText('Загрузка...')).toBeInTheDocument()

  // Завершаем промис имитацией успешного ответа
  resolvePromise!({ id: 1, name: 'Alice' })

  // Ждем пока пользователь отобразится
  await waitFor(() => {
    expect(screen.getByText('Пользователь Alice')).toBeInTheDocument()
  })
})

test('показывает сообщение об ошибке при неуспешной загрузке', async () => {
  ;(api.loadUser as jest.Mock).mockRejectedValue(new Error('Network error'))

  render(<UserInfo userId={1} />)

  await waitFor(() => {
    expect(screen.getByRole('alert')).toHaveTextContent('Ошибка загрузки')
  })
})

// Здесь мы контролируем асинхронный процесс из теста // Сначала проверяем состояние загрузки, затем успешный результат и обработку ошибок

Стратегия покрытия и организация тестов

Что обязательно стоит покрывать

Для компонентного уровня стоит ориентироваться на такие вещи:

  • Важные ветки условной логики (if/else, разные режимы работы).
  • Основные пользовательские сценарии (успешные и неуспешные).
  • Краевые случаи (пустые списки, нулевые значения, отсутствующие данные).

Например, если есть компонент списка:

  • Список с несколькими элементами.
  • Пустой список (нет элементов).
  • Ошибка при загрузке.

Как организовать файлы и имена тестов

Небольшая рекомендация по структуре:

  • Хранить тест рядом с компонентом: Button.tsx и Button.test.tsx.
  • Использовать понятные describe блоки для группировки сценариев (при необходимости).

Пример:

// LoginForm.test.tsx
describe('LoginForm', () => {
  test('отправляет форму с валидными данными', () => {
    // ...
  })

  test('показывает ошибку при пустом пароле', () => {
    // ...
  })

  test('блокирует кнопку во время отправки', () => {
    // ...
  })
})

// Такой формат облегчает ориентацию в тестах // И помогает быстрее понять, какие сценарии уже покрыты

Критерии качества компонентных тестов

Хороший компонентный тест:

  • Понятен с первого прочтения.
  • Отражает реальный сценарий использования.
  • Падает только тогда, когда реально поменялось поведение, а не внутренние детали.
  • Быстро выполняется и не требует реальных внешних ресурсов (сетевых запросов, БД).

Если вы замечаете, что мелкие изменения в верстке ломают десятки тестов, это сигнал, что тесты слишком завязаны на реализацию.

Заключение

Тестирование компонентов помогает удерживать поведение системы под контролем на уровне отдельных, осмысленных частей. Вы проверяете не всю систему целиком, а куски, с которыми постоянно работаете: кнопки, формы, виджеты, сервисы.

Важные идеи, которые стоит вынести:

  • Тестируйте компоненты через их публичный интерфейс: что они получают и что отдают наружу.
  • Сосредотачивайтесь на пользовательском поведении и бизнес-логике, а не на внутренних деталях DOM и реализации.
  • Используйте пропсы и контексты для внедрения зависимостей, чтобы их было легко подменять в тестах.
  • Асинхронные сценарии и сложные взаимодействия нужно тестировать так, чтобы тесты были независимыми от реальной сети или БД.

Компонентные тесты хорошо дополняют модульные и интеграционные тесты: они помогают поймать многие ошибки ещё до того, как вы начнёте гонять end-to-end сценарии. При этом вы остаётесь ближе всего к коду и можете быстро менять реализацию, опираясь на тесты как на «страховочную сетку».

Частозадаваемые технические вопросы по теме статьи и ответы на них

Как понять что логика должна быть протестирована как компонент а что как чистая функция

Если часть логики не зависит от UI и побочных эффектов (например, чистые расчёты, преобразования данных), её проще вынести в отдельную функцию/модуль и покрыть модульными тестами. Логику, которая зависит от состояния компонента, взаимодействий пользователя и рендера, удобнее проверять компонентными тестами.

Как тестировать компоненты которые используют локальное хранилище или cookies

Лучше инкапсулировать работу с localStorage/cookies в отдельный модуль (storageService) и замокать его в тестах. В тесте вы подменяете методы get/set нужным поведением через jest.mock и проверяете, что компонент корректно их вызывает и реагирует на возвращаемые значения.

Что делать если компонент использует таймеры setTimeout или setInterval

В Jest можно включить фейковые таймеры через jest.useFakeTimers. В тесте вы рендерите компонент, запускаете действие, затем перематываете время с помощью jest.advanceTimersByTime или jest.runAllTimers, после чего проверяете ожидаемое состояние или вывод.

Как тестировать компоненты которые сильно зависят от window или document

Если зависимость от глобальных объектов сильная, вынесите такие обращения в отдельные функции (например, getViewportWidth), а в тестах замокайте этот модуль. Если нужно переопределить конкретные свойства window, используйте Object.defineProperty или jest.spyOn для подмены методов, а после теста обязательно восстанавливайте исходное поведение.

Как поступать с компонентами у которых много визуальных состояний и сложная верстка

Вместо того чтобы проверять каждый внутренний div, сосредоточьтесь на сценариях: разные типы данных, пустой/заполненный список, состояния загрузки и ошибки. Для сложной верстки полезно структурировать UI на более мелкие компоненты и тестировать их отдельно, снижая количество сценариев для одного большого компонента.

Стрелочка влевоVite с Vue - практическое руководство для разработчиковТестирование с Jest - практическое руководство для JavaScript и TypeScriptСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Vuex - полное руководство по управлению состоянием во Vue приложенияхРеактивные ссылки ref - полный разбор для разработчиковРеактивные объекты reactive-objects - подробное руководство с примерамиРеактивные переменные - концепция reactive и практические примерыМеханизм Provide Inject - как он работает и когда применятьPinia современный менеджер состояния для VueЛокальное состояние local state в веб разработкеГлобальное состояние в приложениях - global state
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
Функция append в Go GolangХуки жизненного цикла компонентов - полное руководство для разработчиковОтображение компонента mounted - практическое руководствоУничтожение компонента destroyed - как правильно очищать ресурсы и подпискиИнициализация данных в состоянии created - как и когда подготавливать данные в приложенииОбновление компонента beforeUpdate во VueМонтирование компонента - хук beforeMount в VueРазрушение компонента во Vue - beforeDestroy и beforeUnmountСоздание экземпляра beforeCreate - полный разбор жизненного цикла
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Слоты компонента - концепция и практическое использованиеРегистрация компонентов component-registration в приложениях с внедрением зависимостейProps компонента в React - полный разбор с примерамиФункциональные компоненты в React - функциональный подход к построению интерфейсовСобытия компонента - events в современных интерфейсахДинамические компоненты - dynamic-componentsСоздание компонента component - практическое руководствоАсинхронные компоненты async-components - практическое руководство
Наблюдатели watchers - от паттерна до практических реализацийУправление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsШаблоны Vue templates - практическое руководство для разработчиковИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueРеактивность Vue reactivity - как это работает под капотом и как этим пользоватьсяПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueОпции компонента в Go - паттерн component-optionsРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreВычисляемые свойства computed во Vue.jsСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueОсновы Vue - vue-basics для уверенного стартаИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueТелепортация - архитектура и реализация в серверных приложенияхРабота с teleport для управления DOM во VueSuspense в React - управление асинхронными данными и ленивой загрузкойПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиПлагины Vue vue-plugins - полное практическое руководствоРуководство по nextTick для работы с DOMМиксины - mixins в современном программированииJSX в Vue с использованием плагина vue-jsxСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьКастомные элементы - Custom Elements в современном JavaScriptИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий