Я — Solution Architect с 19 годами в IT, часто помогаю новичкам в тестировании. Джуны обычно жалуются на вагон теории, отсутствие практики и примеров, где основные алогритмы собраны вместе. 

Я решил исправить эту проблему: взял OWASP Juice Shop — уязвимое веб-приложение, развернул его через Docker на http://135.181.239.135:4000 и протестировал с помощью ИИ.

В статье расскажу про:

  • ИИ для запуска тест-кейсов: автоматизация без глубоких знаний программирования.

  • Обучение ИИ-генерации тест-кейсов: как ИИ помогает придумывать сценарии.

  • Написание баг-репортов с ИИ: создание чётких и технически точных отчётов.

  • Эксплоративное тестирование с ИИ: поиск уязвимостей с подсказками ИИ.

  • ИИ как учитель: фидбэк и обучение тестированию.

  • Подводные камни ИИ для джунов: чего избегать, чтобы не плодить ошибки.

ИИ для запуска тест-кейсов

Притворюсь джуном-тестировщиком, который тестирует OWASP Juice Shop, развёрнутый в Docker. Задача — проверить форму логина, но навыков написания скриптов на Selenium или Playwright нет. Знаю, как проверять UI, кликать кнопки и заполнять формы, но кодировать это — проблема.

Для решения использую ИИ-инструмент Workik, который генерирует тесты на Cypress по текстовому описанию функциональности. Это упрощает автоматизацию: не нужно искать CSS-селекторы или разбираться с DOM вручную.

Шаг 1: Описываем задачу в Workik

Открываю https://workik.com/cypress-test-case-generator и в поле ввода описываю задачу:

Тестирование формы логина в OWASP Juice Shop на http://135.181.239.135:4000/#/login.
Проверить
:

  1. Успешный вход с email test@test.com и паролем password123.

  2. Ошибка при неверном пароле wrongpass.

  3. Ошибка при пустом email.

Жму «Generate», и Workik выдаёт код на Cypress. Вот пример (с техническими деталями):

// cypress/integration/login.spec.js

describe('Тестирование формы логина в OWASP Juice Shop', () => {

    const baseUrl = 'http://135.181.239.135:4000/#/login';

    beforeEach(() => {

        cy.visit(baseUrl);

        cy.intercept('POST', '/rest/user/login').as('loginRequest');

    });

    it('Успешный вход с валидными данными', () => {

        cy.get('input[type="email"]').type('test@test.com');

        cy.get('input[type="password"]').type('password123');

        cy.get('button[type="submit"]').click();

        cy.wait('@loginRequest').its('response').should('have.property', 'statusCode', 200);

        cy.url().should('eq', 'http://135.181.239.135:4000/#/');

        cy.get('.welcome-message').should('have.text', 'Welcome back!');

    });

    it('Ошибка при неверном пароле', () => {

        cy.get('input[type="email"]').type('test@test.com');

        cy.get('input[type="password"]').type('wrongpass');

        cy.get('button[type="submit"]').click();

        cy.wait('@loginRequest').its('response').should('have.property', 'statusCode', 401);

        cy.get('.error-message').should('have.text', 'Invalid email or password');

    });

    it('Ошибка при пустом email', () => {

        cy.get('input[type="password"]').type('password123');

        cy.get('button[type="submit"]').click();

        cy.get('.error-message').should('have.text', 'Email is required');

        cy.get('input[type="email"]').should('have.class', 'ng-invalid');

    });

});

Технические детали:

  • Используется cy.intercept для перехвата POST-запроса на /rest/user/login, что позволяет проверять HTTP-ответы.

  • Для успешного входа ожидается статус 200 и редирект на главную страницу.

  • Для неверного пароля сервер возвращает 401 Unauthorized, а фронт отображает сообщение об ошибке.

  • При пустом email валидация формы на фронте добавляет класс ng-invalid к полю, а сервер не вызывается.

Шаг 2: Настраиваем окружение и запускаем тесты

Устанавливаю Cypress в проекте:

mkdir juice-shop-tests

cd juice-shop-tests

npm init -y

npm install cypress --save-dev

npx cypress open
Так у меня выглядит эта папка.
Так у меня выглядит эта папка.
А так выглядит запущенный cypress:
А так выглядит запущенный cypress:
Cypress открывает окно, где я выбираю E2E Testing
Cypress открывает окно, где я выбираю E2E Testing
Дальше соглашаюсь со стандартной конфигурацией и открываю Chrome:
Дальше соглашаюсь со стандартной конфигурацией и открываю Chrome:
Окно выбора создания файла конфигурации
Окно выбора создания файла конфигурации

Создаю новый файл конфигурации login.spec.cy.js, вставляю код от Workik и запускаю тесты. Cypress открывает браузер, выполняет действия и проверяет результаты. Если тест падает, в логах видно:

  • URL запроса (/rest/user/login).

  • HTTP-статус (например, 401 для неверного пароля).

  • Тело ответа (например, {"error": "Invalid email or password"}).

Пример ошибки: Тест на неверный пароль падает, если сервер возвращает не Invalid email or password, а Wrong credentials. Лог Cypress показывает:

GET /rest/user/login 401

Response: {"error": "Wrong credentials"}

Исправляю ожидание:

cy.get('.error-message').should('have.text', 'Wrong credentials');

Шаг 3: Учимся на результатах

Добавляю тест на SQL-инъекцию через Workik, указав: «Проверить SQL-инъекцию с email ' OR '1'='1 используя cypress. Получаю:

describe('SQL Injection Test on Login', () => {
    it('should attempt SQL injection with email', () => {
        // Переход к странице логина
        cy.visit('http://135.181.239.135:4000/#/login');

        // Ввод вредоносного email
        cy.get('input[name="email"]').type("' OR '1'='1");

        // Ввод случайного пароля
        cy.get('input[name="password"]').type('any_password');

        // Нажимаем на кнопку логина
        cy.get('button').contains('Login').click();

        // Ожидаем и проверяем наличие сообщения об успешном входе
        cy.url().should('not.include', '/login'); // Проверка, что мы не на странице логина

        // Проверка, что сообщение об успешном логине или изменение в интерфейсе происходит
        cy.contains('Logged in').should('exist');
    });
});

Технический анализ:

  • Сервер корректно экранирует ввод, возвращая 401 вместо 200, что исключает возможность обхода авторизации.

  • Отсутствие SQL-ошибок в ответе (SQL syntax error) подтверждает защиту от инъекций.

  • Лог Cypress показывает, что запрос ушёл с экранированным телом: email=%27%20OR%20%271%27%3D%271.

Шаг 4: Интеграция в CI/CD

Добавляю тесты в GitHub Actions для автоматического прогона:

name: Cypress Tests

on:
  push:
    branches:
      - main  # Укажите вашу основную ветку

jobs:
  cypress-run:
    runs-on: ubuntu-latest

    steps:
      # 1. Шаг: Проверка исходного кода
      - name: Checkout code
        uses: actions/checkout@v3

      # 2. Шаг: Настройка Node.js
      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'

      # 3. Шаг: Установка зависимостей
      - name: Install dependencies
        run: npm install

      # 4. Шаг: Запуск тестов Cypress в headless-режиме
      - name: Run Cypress tests
        run: npx cypress run --browser chrome --headless
        # Настройка сохранения артефактов
        continue-on-error: true # Позволяет процессу завершаться, даже если тесты не проходят

      # 5. Шаг: Сохранение артефактов (логи, скриншоты, видео)
      - name: Upload Cypress artifacts
        uses: actions/upload-artifact@v3
        with:
          name: cypress-artifacts
          path: |
            cypress/videos
            cypress/screenshots
            cypress/results

Технические детали:

  • Cypress запускается в headless-режиме с Chrome.

  • Логи и артефакты (скриншоты, видео) сохраняются в GitHub Actions для анализа.

Плюсы:

  • Workik генерирует код, совместимый с CI/CD.

  • Логи HTTP-запросов помогают дебагать серверные ошибки.

Подводные камни:

  • Селекторы (например, .error-message) могут ломаться при редизайне фронта.

  • Бесплатная версия Workik ограничивает количество генераций.

Учимся через ИИ-генерацию тест-кейсов

Форма логина кажется простой, но тестировщик должен проверять краевые случаи: пустые поля, SQL-инъекции, длинные строки. Workik помогает генерировать такие сценарии.

Шаг 1: Описываем задачу

В Workik пишу:

Workik выдаёт код:

describe('Login Form Tests', () => {
    beforeEach(() => {
        cy.visit('http://135.181.239.135:4000/#/login');  // Переход к странице логина
    });

    it('should successfully log in with valid credentials', () => {
        cy.get('input[name="email"]').type('test@test.com');  // Ввод email
        cy.get('input[name="password"]').type('password123');  // Ввод пароля
        cy.get('button').contains('Login').click();  // Нажимаем вход

        cy.url().should('not.include', '/login');  // Проверяем, что не на странице логина
        cy.contains('Logged in').should('exist');  // Проверка на наличие сообщения о входе
    });

    it('should display error for incorrect password', () => {
        cy.get('input[name="email"]').type('test@test.com');  // Ввод email
        cy.get('input[name="password"]').type('wrongpass');  // Неверный пароль
        cy.get('button').contains('Login').click();  // Нажимаем вход

        cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки
    });

    it('should display error for empty email', () => {
        cy.get('input[name="email"]').type('');  // Пустой email
        cy.get('input[name="password"]').type('password123');  // Ввод пароля
        cy.get('button').contains('Login').click();  // Нажимаем вход

        cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки
    });

    it('should handle SQL injection attempt', () => {
        cy.get('input[name="email"]').type("' OR '1'='1");  // SQL-инъекция
        cy.get('input[name="password"]').type('any_password');  // Неверный пароль
        cy.get('button').contains('Login').click();  // Нажимаем вход

        cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки
    });

    it('should display error for long email', () => {
        const longEmail = 'a'.repeat(101) + '@test.com';  // Длинный email (>100 символов)
        cy.get('input[name="email"]').type(longEmail);  // Ввод длинного email
        cy.get('input[name="password"]').type('password123');  // Ввод пароля
        cy.get('button').contains('Login').click();  // Нажимаем вход

        cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки
    });
});

Технический анализ:

  • Тест на длинный email падает: сервер возвращает 500 вместо ожидаемого 400 Bad Request.

  • Лог Cypress: POST /rest/user/login 500, тело ответа: {"error": "Uncaught exception in user validation"}.

  • Это указывает на отсутствие серверной валидации длины строки, что приводит к неперехваченному исключению.

Шаг 2: Запускаем и анализируем

Запускаю тесты (npx cypress open). Тест с длинным email падает. Лог:

POST /rest/user/login 500

Response: {"error": "Uncaught exception in user validation"}

Исправляю ожидание:

cy.get('#errorMessage').should('contain', 'Internal Server Error');

Технический вывод:

  • Сервер не валидирует длину email, что вызывает исключение в бэкенде (вероятно, в Node.js/Express).

  • Рекомендация: добавить валидацию на уровне middleware (например, express-validator).

Шаг 3: Учимся думать как тестировщик

Workik подсказывает неочевидные случаи, вроде SQL-инъекций. Анализ логов учит:

  • Проверять HTTP-статусы (200, 401, 500).

  • Искать несоответствия между фронтом и бэкендом.

Упрощаем баг-репорты с ИИ

Найти баг — полдела. Нужно описать его так, чтобы разработчики поняли проблему. Пример: тест на длинный email выявил ошибку 500.

Шаг 1: Ловим баг

Тест:

it('Длинный email', () => {
    const longEmail = 'a'.repeat(100) + '@test.com';
    cy.get('#email').type(longEmail);
    cy.get('#password').type('password123');
    cy.get('#loginButton').click();
    cy.wait('@loginRequest').its('response.statusCode').should('eq', 500);
});

Лог:

  • POST /rest/user/login 500

  • Тело ответа: {"error": "Uncaught exception in user validation"}

  • Скриншот: сообщение «Internal Server Error».

Шаг 2: Собираем баг-репорт

Баг-репорт:

Название: Ошибка 500 при отправке длинного email в форме логина
Шаги воспроизведения:

  1. Открыть http://135.181.239.135:4000/#/login.

  2. В поле email ввести строку длиной >100 символов (например, a...a@test.com).

  3. В поле пароля ввести password123.

  4. Нажать «Log in».

Ожидаемое поведение:

  • Сервер возвращает 400 Bad Request с сообщением Email too long.

  • Фронт отображает ошибку валидации.

Фактическое поведение:

  • Сервер возвращает 500 Internal Server Error.

  • Тело ответа: {"error": "Uncaught exception in user validation"}.

  • Фронт показывает «Internal Server Error».

Логи:

  • POST /rest/user/login 500

  • Запрос: {"email": "a...a@test.com", "password": "password123"}

  • Ответ: {"error": "Uncaught exception in user validation"}

Скриншоты: [Скриншот из cypress/screenshots]
Окружение:

Технический анализ:

  • Ошибка указывает на отсутствие валидации длины email в бэкенде.

  • Возможная причина: необработанное исключение в функции валидации (например, TypeError при работе со строкой).

  • Рекомендация: добавить проверку длины email в middleware или модели пользователя.

Шаг 3: Упрощаем с ИИ

Инструменты вроде BugBug анализируют логи и предлагают текст репорта. Пример:

Test failed: Expected 400, got 500

Suggested report:

- Steps: Send POST /rest/user/login with email >100 chars

- Expected: 400 Bad Request

- Actual: 500 Internal Server Error

- Logs: {"error": "Uncaught exception"}

Эксплоративное тестирование с ИИ

Эксплоративное тестирование — это исследование приложения без строгих тест-кейсов. В Juice Shop полно уязвимостей (XSS, SQL-инъекции), и ИИ помогает их найти.

Шаг 1: Получаем подсказки

В Workik пишу:

Исследовать OWASP Juice Shop на http://135.181.239.135:4000. Найти уязвимости в форме логина, корзине, поиске. Подсказать сценарии.

Workik предлагает:

Форма логина:

  • SQL-инъекция: Ввести ' OR '1'='1.

  • XSS: Ввести <script>alert('hack')</script>.

  • Переполнение буфера: Длинный email (>1000 символов).

Корзина:

  • Манипуляция количеством: Изменить поле quantity через DevTools.

  • CSRF: Проверить отсутствие токена в запросах.

Поиск:

  • XSS: Ввести <script>alert('boom')</script>.

  • SQL-инъекция: Ввести %' OR '1'='1.

Шаг 2: Исследуем

Проверяю XSS в форме логина:

Шаги:

  1. В поле email ввожу <script>alert('hack')</script>.

  2. Жму «Log in».

Результат:

  • Появляется alert с текстом hack.

  • Лог: скрипт выполняется без экранирования.

Баг-репорт:

Название: XSS-уязвимость в форме логина
Шаги:

  1. Открыть http://135.181.239.135:4000/#/login.

  2. В поле email ввести <script>alert('hack')</script>.

  3. Нажать «Log in».

Ожидаемое поведение:

  • Ввод экранируется, alert не выполняется.

  • Сервер возвращает 400 или 401.

Фактическое поведение:

  • Выполняется JavaScript (alert).

  • Ответ сервера: 200 OK (ввод не валидируется).

Логи:

  • POST /rest/user/login 200

  • Тело запроса: {"email": "<script>alert('hack')</script>", "password": "password123"}

Технический анализ:

  • Отсутствует экранирование ввода на сервере (например, нет sanitize-html).

  • Рекомендация: добавить Content Security Policy (CSP) и экранирование.

ИИ как учитель

ИИ учит через фидбэк. Например, тест на длинный email падает. Лог:

POST /rest/user/login 500

{"error": "Uncaught exception"}

Workik подсказывает: «Проверь серверные логи». Я нахожу в логах Juice Shop:

TypeError: Cannot read property 'length' of undefined

    at validateEmail (/app/server.js:123)

Урок:

  • Проблема в функции validateEmail, которая не проверяет тип входных данных.

  • Учусь анализировать стек вызовов.

Подводные камни

  • Слепая вера: Workik может упустить XSS в поиске, если не указать конкретно.

  • Ограничения бесплатной версии: Нет анализа серверных логов.

  • Контекст: ИИ не знает о скрытых API-уязвимостях Juice Shop.


А если вам нужен ИИ-помощник для сотрудника — познакомьтесь ближе с продуктами Minervasoft. Ассистент с генеративным ИИ Minerva Copilot встраивается в любую систему компании и подсказывает ответы из корпоративной базы знаний в зависимости от контекста. Быстро, качественно и со ссылками на статьи.

Кроме того, благодаря технологии DataHub система управления знаниями Minerva Knowledge становится корпоративным «мозгом» для GenAI. Платформа объединяет любые источники информации в компании, в том числе внутренние системы, базы знаний и другие хранилища данных.

Узнать подробнее

Комментарии (0)