Изолированное тестирование Vue/React в 1С-Битрикс: настройка Jest
Представьте: вы обновляете компонент корзины, merge в develop, а через час прод падает — на тестовом стенде всё работало. Знакомо? Изолированные тесты ловят такие регрессии за секунды, без сервера и базы данных. Официальная документация рекомендует изолировать тестирование компонентов с помощью моков. Руководство разработчика 1С-Битрикс Мы настраиваем Jest для Vue/React компонентов в проектах на 1С-Битрикс: моки для BX/BX24, интеграция в CI, покрытие ключевых сценариев. Работаем по договору, гарантируем результат. По нашим оценкам, внедрение изолированных тестов сокращает затраты на отлов багов на 30–50%, что для среднего интернет-магазина на Битриксе означает экономию от 200 000 до 500 000 рублей в год. Ручное тестирование одного модуля в среднем обходится компании в 30 000–50 000 рублей в месяц при полной загрузке тестировщика. Тесты выполняются за миллисекунды и запускаются в любой среде.
Проблемы, которые решает изолированное тестирование
Регрессии при изменении шаблонов. Например, после обновления компонента корзины ломается отображение суммы — тест ловит это до деплоя. Скрытые баги в composable/хуках. Логика корзины, фильтрации, пагинации — если не покрыта тестами, ошибка всплывёт только в проде. Долгое ручное тестирование. При каждом коммите QA проверяет десятки сценариев вручную. Отсутствие CI-проверок. Без тестов нельзя гарантировать, что фронтенд не сломан после слияния веток.
Кроме того, типичная проблема — некорректная работа с объектом BX после обновления CMS. Моки позволяют симулировать API без реального сервера, что упрощает отладку.
Как мы настраиваем Jest для проекта на Битриксе?
Мы сертифицированные специалисты 1С-Битрикс. Опыт — 10+ лет в проектах любой сложности. Настройка включает:
- Конфигурацию Jest с TypeScript, jsdom, покрытием.
- Моки для глобальных объектов
BX,BX24,BX.ajax. - Тесты для 5–10 приоритетных компонентов (корзина, карточка товара, фильтр).
- Интеграцию в GitLab CI / GitHub Actions.
- README с описанием запуска и правилами добавления новых тестов.
Вот типичная структура тестов в Битрикс-проекте
/local/templates/my_site/ src/ components/ catalog/ ProductCard.vue ProductCard.test.ts cart/ CartItem.tsx CartItem.test.tsx composables/ useCart.ts useCart.test.ts jest.config.ts package.json Тесты рядом с компонентами — удобнее, чем отдельная папка: при рефакторинге переносим вместе.
Пример конфигурации jest.config.ts для Vue + TypeScript
import type { Config } from 'jest'; const config: Config = { testEnvironment: 'jsdom', transform: { '^.+\\.vue$': ['@vue/vue3-jest', { tsConfig: 'tsconfig.json' }], '^.+\\.(ts|tsx|js|jsx)$': ['ts-jest', { tsconfig: 'tsconfig.json' }], }, moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(css|scss|png|jpg|svg)$': '<rootDir>/src/__mocks__/fileMock.ts', '^bx-globals$': '<rootDir>/src/__mocks__/bx.ts', }, moduleFileExtensions: ['ts', 'tsx', 'vue', 'js', 'json'], coverageDirectory: 'coverage', collectCoverageFrom: [ 'src/components/**/*.{vue,ts,tsx}', 'src/composables/**/*.ts', '!src/**/*.test.{ts,tsx}', ], setupFilesAfterFramework: ['<rootDir>/src/test-setup.ts'], }; export default config; Мок глобального объекта BX и BX24
// src/__mocks__/bx.ts global.BX = { bitrix_sessid: () => 'test-sessid-12345', message: (params: Record<string, string>) => params, bind: jest.fn(), Event: { add: jest.fn() }, }; global.BX24 = { init: (cb: () => void) => cb(), isAdmin: () => false, callMethod: jest.fn(), callBatch: jest.fn(), resizeWindow: jest.fn(), }; Почему стоит тестировать изолированно, а не интеграционно?
Изолированные тесты не требуют базы данных, сервера или запущенного Битрикса. Они выполняются за миллисекунды, их легко запускать локально и в CI. Интеграционные тесты (например, через Selenium) медленнее и нестабильнее. Мы рекомендуем пирамиду: 70% unit-тестов, 20% компонентных, 10% e2e.
| Параметр | Изолированные тесты | Интеграционные тесты |
|---|---|---|
| Скорость | миллисекунды | минуты |
| Зависимости | только Node.js | сервер, БД, браузер |
| Надёжность | высокая | средняя (flaky) |
| Запуск в CI | без проблем | требуется инфраструктура |
Примеры тестов
Vue-компонент ProductCard
// src/components/catalog/ProductCard.test.ts import { mount } from '@vue/test-utils'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import ProductCard from './ProductCard.vue'; import * as cartApi from '@/api/cart'; const mockProduct = { id: '42', name: 'Дрель Bosch GSB 21-2 RCT', price: '8990', currency: 'RUB', img: '/upload/test.jpg', inStock: true, }; describe('ProductCard', () => { it('отображает название и цену товара', () => { const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); expect(wrapper.find('.product-name').text()).toBe(mockProduct.name); expect(wrapper.find('.product-price').text()).toContain('8 990'); }); it('показывает кнопку «В корзину» для товара в наличии', () => { const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); expect(wrapper.find('[data-action="add-to-cart"]').exists()).toBe(true); expect(wrapper.find('.out-of-stock').exists()).toBe(false); }); it('скрывает кнопку «В корзину» для товара не в наличии', () => { const wrapper = mount(ProductCard, { props: { product: { ...mockProduct, inStock: false } }, }); expect(wrapper.find('[data-action="add-to-cart"]').exists()).toBe(false); expect(wrapper.find('.out-of-stock').exists()).toBe(true); }); it('вызывает API корзины при клике «В корзину»', async () => { const addToCart = vi.spyOn(cartApi, 'addToCart').mockResolvedValue({ items: [], totalPrice: 8990, totalCount: 1, currency: 'RUB', }); const wrapper = mount(ProductCard, { props: { product: mockProduct }, }); await wrapper.find('[data-action="add-to-cart"]').trigger('click'); await wrapper.vm.$nextTick(); expect(addToCart).toHaveBeenCalledWith({ productId: 42, quantity: 1, }); }); }); React-компонент CartItem
// src/components/cart/CartItem.test.tsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import CartItem from './CartItem'; import * as cartApi from '@/api/cart'; const mockItem = { id: 1, name: 'Перфоратор Makita HR2630', price: 12490, quantity: 2, img: null, }; describe('CartItem', () => { it('отображает название и суммарную стоимость', () => { render(<CartItem item={mockItem} onRemove={jest.fn()} onQuantityChange={jest.fn()} />); expect(screen.getByText('Перфоратор Makita HR2630')).toBeInTheDocument(); expect(screen.getByText('24 980 ₽')).toBeInTheDocument(); }); it('вызывает onQuantityChange при изменении количества', async () => { const onQuantityChange = jest.fn(); const user = userEvent.setup(); render(<CartItem item={mockItem} onRemove={jest.fn()} onQuantityChange={onQuantityChange} />); const plusBtn = screen.getByRole('button', { name: '+' }); await user.click(plusBtn); expect(onQuantityChange).toHaveBeenCalledWith(mockItem.id, 3); }); }); Как добавить тесты в CI?
- Настройте джобу в GitLab CI / GitHub Actions: установка зависимостей, запуск
jest --coverage. - Добавьте порог покрытия в jest.config.ts:
coverageThreshold. - Настройте артефакты для отчёта о покрытии.
- При неудачных тестах — блокировка merge request.
Что входит в работу
| Этап | Что делаем | Срок |
|---|---|---|
| Аудит текущего фронтенда | Определяем список компонентов, composable, API-слоёв | 4–8 часов |
| Настройка Jest + моков | Конфигурация, файлы setup, моки BX/BX24/static assets | 1 день |
| Написание тестов для приоритетных компонентов | 5–10 компонентов с покрытием ключевых сценариев | 1–2 дня |
| Интеграция в CI | Добавление джобы в GitLab/GitHub Actions, настройка coverage-репорта | 4 часа |
| Документация и обучение | README с инструкциями, демо-сессия для команды | 2–4 часа |
Чек-лист для внедрения тестов
- Собрать список компонентов, composable и API-слоёв. - Настроить Jest с TypeScript и jsdom. - Создать моки для BX, BX24, статики. - Написать тесты для топ-5 компонентов. - Интегрировать в CI с порогом покрытия. - Провести ревью с командой.Получите консультацию по настройке Jest для вашего проекта — мы оценим объем и сроки бесплатно. Свяжитесь с нами — подготовим предложение и гарантируем качество: все тесты проходят в CI, покрытие не ниже заданного порога.







