Почему Jenkins важен для QA

Jenkins — самый широко развёрнутый CI/CD-сервер в мире. С более чем 1800 плагинами и огромным сообществом он остаётся основой пайплайнов автоматизации тестирования в тысячах компаний — от стартапов до таких предприятий как Netflix и Airbnb.

Как QA-инженер, вы почти наверняка столкнётесь с Jenkins в своей карьере. Даже если ваша текущая команда использует другой инструмент, понимание Jenkins даёт передаваемые знания о проектировании CI/CD-пайплайнов, применимые повсюду.

Архитектура Jenkins

Jenkins работает на архитектуре controller-agent:

  • Controller (Master): Управляет веб-интерфейсом, планирует сборки, распределяет задания по агентам и хранит результаты
  • Agents (Nodes): Выполняют реальную работу по сборке и тестированию. Агенты могут быть физическими машинами, виртуальными машинами, Docker-контейнерами или Kubernetes-подами

Это разделение критично для QA, потому что выполнение тестов может быть ресурсоёмким. Запуск Selenium-тестов на контроллере замедлит всё. Выделенные агенты обеспечивают тесты необходимыми ресурсами без влияния на другие задания.

Ключевые концепции

КонцепцияОписание
JobНастроенная задача автоматизации (сборка, тест, деплой)
PipelineСерия этапов, определённых в коде (Jenkinsfile)
StageЛогическая группировка шагов (напр., “Build”, “Test”, “Deploy”)
StepОтдельная команда или действие внутри stage
BuildОдно выполнение job/pipeline
WorkspaceКаталог, где Jenkins загружает код и выполняет шаги
ArtifactФайлы, сохранённые из сборки (отчёты тестов, скриншоты, логи)

Декларативный Jenkinsfile

Современный Jenkins использует декларативные Jenkinsfile — определения пайплайнов, хранящиеся в репозитории проекта. Это подход pipeline-as-code.

Базовая структура

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'npm ci'
            }
        }
        stage('Unit Tests') {
            steps {
                sh 'npm test'
            }
        }
        stage('E2E Tests') {
            steps {
                sh 'npx playwright test'
            }
        }
    }

    post {
        always {
            junit '**/test-results/*.xml'
            archiveArtifacts artifacts: '**/test-reports/**', allowEmptyArchive: true
        }
        failure {
            echo 'Пайплайн упал! Проверьте отчёты тестов.'
        }
    }
}

Конфигурация агентов

Директива agent указывает Jenkins, где выполнять пайплайн:

// Выполнить на любом доступном агенте
agent any

// Выполнить на агенте с определённой меткой
agent { label 'linux && chrome' }

// Выполнить внутри Docker-контейнера
agent {
    docker {
        image 'mcr.microsoft.com/playwright:v1.40.0-focal'
    }
}

Для QA Docker-агенты особенно полезны, потому что они предоставляют консистентное окружение с предустановленными браузерами и инструментами тестирования.

Переменные окружения

pipeline {
    agent any

    environment {
        BASE_URL = 'https://staging.example.com'
        TEST_ENV = 'ci'
        BROWSER = 'chromium'
    }

    stages {
        stage('E2E Tests') {
            steps {
                sh 'npx playwright test --project=${BROWSER}'
            }
        }
    }
}

Используйте credentials() для чувствительных значений:

environment {
    DB_CREDS = credentials('test-database-credentials')
    API_KEY = credentials('api-key-staging')
}

Параллельное выполнение тестов

Запуск тестов параллельно — одна из наиболее эффективных оптимизаций для QA-пайплайнов. Jenkins поддерживает параллелизацию на уровне stage:

stage('Tests') {
    parallel {
        stage('Тесты Chrome') {
            agent { label 'chrome' }
            steps {
                sh 'npx playwright test --project=chromium'
            }
        }
        stage('Тесты Firefox') {
            agent { label 'firefox' }
            steps {
                sh 'npx playwright test --project=firefox'
            }
        }
        stage('API-тесты') {
            agent any
            steps {
                sh 'npm run test:api'
            }
        }
    }
}

Это запускает тесты Chrome, Firefox и API одновременно на разных агентах. Если каждый набор занимает 15 минут, параллельный этап завершается за 15 минут вместо 45 минут последовательно.

Отчёты о тестах

Jenkins предоставляет несколько способов отображения результатов тестов.

Плагин JUnit Report

Большинство тестовых фреймворков могут генерировать формат JUnit XML. Jenkins может парсить эти отчёты и показывать результаты в панели сборки:

post {
    always {
        junit testResults: '**/junit-results.xml', allowEmptyResults: true
    }
}

HTML Publisher

Для богатых тестовых отчётов (Allure, Playwright HTML reporter):

post {
    always {
        publishHTML([
            reportDir: 'playwright-report',
            reportFiles: 'index.html',
            reportName: 'Отчёт Playwright'
        ])
    }
}

Архивация артефактов

Сохранение скриншотов, видео и логов упавших тестов:

post {
    always {
        archiveArtifacts artifacts: 'test-results/**', allowEmptyArchive: true
    }
    failure {
        archiveArtifacts artifacts: 'screenshots/**', allowEmptyArchive: true
    }
}

Quality Gates в Jenkins

Quality gates определяют критерии, которые должны быть выполнены для продолжения пайплайна. Для QA типичные gates включают:

Процент прохождения тестов

stage('Quality Gate') {
    steps {
        script {
            def testResults = currentBuild.rawBuild.getAction(hudson.tasks.junit.TestResultAction.class)
            if (testResults) {
                def failCount = testResults.result.failCount
                if (failCount > 0) {
                    error("${failCount} тестов упало. Сборка заблокирована.")
                }
            }
        }
    }
}

Упражнение: Создайте Jenkins-пайплайн для веб-приложения

Спроектируйте полный Jenkinsfile для веб-приложения со следующими требованиями:

  • Приложение Node.js с React-фронтендом и Express-бэкендом
  • Unit-тесты (Jest), API-тесты (Supertest) и E2E-тесты (Playwright)
  • Тесты должны выполняться параллельно где возможно
  • Деплой в staging после прохождения всех тестов
  • Quality gate: ноль падений тестов и минимум 80% покрытия кода
Решение
pipeline {
    agent { docker { image 'node:20-slim' } }

    environment {
        CI = 'true'
        BASE_URL = 'https://staging.example.com'
    }

    stages {
        stage('Install') {
            steps {
                sh 'npm ci'
            }
        }

        stage('Lint & Type Check') {
            steps {
                sh 'npm run lint'
                sh 'npm run type-check'
            }
        }

        stage('Tests') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm run test:unit -- --coverage --ci'
                    }
                    post {
                        always {
                            junit 'coverage/junit.xml'
                            publishCoverage adapters: [coberturaAdapter('coverage/cobertura-coverage.xml')]
                        }
                    }
                }
                stage('API Tests') {
                    steps {
                        sh 'npm run test:api'
                    }
                    post {
                        always {
                            junit 'api-test-results/junit.xml'
                        }
                    }
                }
                stage('E2E Tests') {
                    agent {
                        docker { image 'mcr.microsoft.com/playwright:v1.40.0-focal' }
                    }
                    steps {
                        sh 'npm ci'
                        sh 'npx playwright test'
                    }
                    post {
                        always {
                            junit 'test-results/junit.xml'
                            publishHTML([
                                reportDir: 'playwright-report',
                                reportFiles: 'index.html',
                                reportName: 'Отчёт Playwright'
                            ])
                            archiveArtifacts artifacts: 'test-results/**', allowEmptyArchive: true
                        }
                    }
                }
            }
        }

        stage('Quality Gate') {
            steps {
                script {
                    def testResults = currentBuild.rawBuild.getAction(hudson.tasks.junit.TestResultAction.class)
                    if (testResults && testResults.result.failCount > 0) {
                        error("${testResults.result.failCount} тестов упало.")
                    }
                }
            }
        }

        stage('Deploy to Staging') {
            when { branch 'main' }
            steps {
                sh './scripts/deploy-staging.sh'
            }
        }
    }

    post {
        always {
            cleanWs()
        }
        failure {
            echo 'Пайплайн упал. Проверьте отчёты тестов выше.'
        }
    }
}

Популярные плагины Jenkins для QA

ПлагинНазначение
JUnitПарсинг и отображение результатов тестов
HTML PublisherПубликация HTML-отчётов
AllureПродвинутая агрегация отчётов тестов
CoverageВизуализация покрытия кода
Pipeline Utility StepsОперации с файлами, парсинг JSON
Docker PipelineЗапуск этапов в Docker-контейнерах
Slack NotificationОтправка результатов сборки в Slack
GitУправление исходным кодом
Credentials BindingБезопасная инъекция секретов в сборки
Blue OceanСовременный UI Jenkins с визуализацией пайплайна

Лучшие практики Jenkins для QA

  1. Всегда используйте декларативные Jenkinsfile, хранящиеся в репозитории. Никогда не настраивайте пайплайны через UI Jenkins — это не версионируется.

  2. Держите выполнение пайплайна в пределах 30 минут. Если тесты занимают больше, параллелизуйте их или разделите на отдельные пайплайны (быстрый feedback + ночная полная регрессия).

  3. Используйте Docker-агенты для консистентных тестовых окружений. Фиксируйте конкретные версии образов, чтобы избежать сюрпризов при обновлении.

  4. Архивируйте тестовые артефакты при каждой сборке — не только при падениях. Исторические данные нужны для анализа тенденций.

  5. Реализуйте корректную очистку с cleanWs() в блоке post { always }. Оставшиеся файлы от предыдущих сборок могут вызывать нестабильные тесты.

  6. Используйте shared libraries для общего кода пайплайнов. Если несколько проектов используют одинаковую настройку отчётности тестов, вынесите её в общую библиотеку.

Устранение типичных проблем

Тесты проходят локально, но падают в Jenkins

  • Различие окружений: Используйте Docker-агенты для соответствия локальному окружению
  • Отсутствующие зависимости: Убедитесь, что npm ci (а не npm install) запускается в CI
  • Проблемы с таймингом: CI-агенты могут быть медленнее; увеличьте таймауты для E2E-тестов
  • Проблемы отображения: Headless-режим браузера может вести себя иначе; проверьте скриншоты

Пайплайн работает медленно

  • Нет параллелизации: Разделите независимые тестовые наборы на параллельные этапы
  • Нет кэширования: Используйте кэширование workspace Jenkins или кэширование слоёв Docker для node_modules
  • Конкуренция за ресурсы: Убедитесь, что агенты имеют достаточно CPU/RAM для браузерных тестов

Нестабильные тесты в CI

  • Общее состояние: Тесты, использующие общую БД или файлы, могут мешать друг другу
  • Состояния гонки: Сетевые запросы могут падать по таймауту под нагрузкой CI
  • Решение: Изолируйте тестовые данные, добавьте retry для сетезависимых операций, используйте playwright test --retries=1