Введение

Появление ChatGPT и других больших языковых моделей (LLM) в 2022-2023 годах создало новую волну хайпа вокруг AI в тестировании. Обещания заманчивы: AI (как обсуждается в AI Copilot for Test Automation: GitHub Copilot, Amazon CodeWhisperer and the Future of QA) генерирует тесты, данные, находит баги, пишет документацию. Но что реально работает сейчас, а что остается маркетингом?

В этой статье мы разберем практическое применение LLM (как обсуждается в Test Automation with Claude and GPT-4: Real Integration Cases and Practical Implementation) в QA: где они действительно помогают, как их использовать эффективно, и — что критически важно — где они могут навредить. После года активного использования GPT-4, Claude, и других моделей в production QA процессах, у нас достаточно данных для честного анализа.

Что такое LLM и почему они важны для QA

Large Language Models explained

LLM (Large Language Model) — это нейронная сеть, обученная на огромных объемах текста предсказывать следующее слово.

Ключевые модели 2025:

  • GPT-4 (OpenAI) (как обсуждается в Prompt Engineering for QA: Mastering Effective AI Queries): Самая мощная, лучший reasoning
  • Claude 3 (Anthropic): Большой context window (200K tokens), хорош для кода
  • Gemini Pro (Google): Multimodal, интеграция с Google Workspace
  • Llama 3 (Meta): Open-source, можно запускать локально

Почему LLM = game changer для QA

1. Natural Language Understanding:

Human: "Создай тесты для login flow с валидацией email"

LLM: ✓ Понимает контекст ("login flow")
     ✓ Знает что такое email валидация
     ✓ Может сгенерировать код на любом языке

2. Code Generation:

  • Пишет тесты на Selenium, Cypress, Playwright
  • Генерирует test data
  • Создает mock responses

3. Context-aware Analysis:

  • Анализирует код и предлагает edge cases
  • Находит потенциальные баги в логике
  • Генерирует documentation

Но: LLM не “думают”, они статистически предсказывают. Это важно помнить.

Генерация тест-данных

Realistic test data generation

Проблема: Создание реалистичных тестовых данных вручную занимает часы.

Решение с LLM:

# Промпт для ChatGPT
"""
Generate 50 realistic test users for an e-commerce platform.
Include: name, email, age, country, payment_method, loyalty_tier.
Make data diverse (different countries, age groups).
Output as JSON array.
"""

# GPT-4 response:
[
  {
    "name": "Maria Garcia",
    "email": "m.garcia82@gmail.com",
    "age": 34,
    "country": "Spain",
    "payment_method": "credit_card",
    "loyalty_tier": "gold"
  },
  {
    "name": "Kenji Tanaka",
    "email": "kenji.t@yahoo.co.jp",
    "age": 28,
    "country": "Japan",
    "payment_method": "paypal",
    "loyalty_tier": "silver"
  },
  // ... 48 more realistic users
]

Преимущества:

  • Данные разнообразные и реалистичные
  • Учитывает культурные особенности (японские имена, испанские фамилии)
  • Генерация за секунды вместо часов

Advanced: Contextual test data

# Используем LangChain для programmatic generation
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate

llm = ChatOpenAI(model="gpt-4", temperature=0.7)

prompt = PromptTemplate(
    input_variables=["scenario", "count"],
    template="""
    Generate {count} test data entries for scenario: {scenario}

    Requirements:
    - Data must be realistic and diverse
    - Include edge cases (empty strings, special chars, very long values)
    - Cover positive and negative test cases
    - Output as JSON
    """
)

# Generate data for specific scenario
test_data = llm.predict(
    prompt.format(
        scenario="User registration with international phone numbers",
        count=30
    )
)

Результат:

[
  {"phone": "+1-555-0123", "country": "US", "valid": true},
  {"phone": "+44 20 7946 0958", "country": "UK", "valid": true},
  {"phone": "+81-3-1234-5678", "country": "Japan", "valid": true},
  {"phone": "not-a-phone", "country": "US", "valid": false},
  {"phone": "", "country": "US", "valid": false},
  {"phone": "+1" + "5"*100, "country": "US", "valid": false},  // Edge case
  // ...
]

Domain-specific data

E-commerce product data:

Prompt: Generate 20 product listings for outdoor gear store.
Include: name, description, price, category, stock, ratings.
Make descriptions SEO-friendly and realistic.

Financial test data:

"""
Generate 15 bank transaction records for fraud detection testing.
Include:
- 10 legitimate transactions
- 5 suspicious transactions (unusual amounts, locations, patterns)
Mark each with 'is_fraud' flag and reason.
"""

LLM понимает что такое “suspicious pattern” и генерирует:

  • Транзакция на $10,000 в 3am из нового location
  • Множественные small transactions подряд (card testing)
  • Purchase в двух разных странах за короткое время

Создание тест-кейсов с помощью AI

From requirements to tests

Traditional process:

  1. Читаешь requirements (30 min)
  2. Придумываешь тест-кейсы (1-2 hours)
  3. Пишешь тесты (2-3 hours)

With LLM:

Prompt:
---
User Story:
"As a user, I want to reset my password via email so that I can regain access if I forget it."

Acceptance Criteria:
- User enters email address
- System sends reset link to email
- Link expires after 24 hours
- User sets new password (min 8 chars, 1 uppercase, 1 number)
- Old password no longer works

Generate comprehensive test cases covering positive, negative, and edge cases.
---

GPT-4 Output:

Test Cases:

POSITIVE SCENARIOS:
TC1: Valid password reset flow
  Steps:
    1. Navigate to "Forgot Password" page
    2. Enter valid registered email
    3. Click "Send Reset Link"
    4. Check email inbox
    5. Click reset link
    6. Enter new valid password: "NewPass123"
    7. Confirm password
    8. Submit
  Expected: Password reset successful, can login with new password

TC2: Reset link works within 24h window
  Steps: [Similar to TC1, but verify link works at 23:59 mark]
  Expected: Link still functional

NEGATIVE SCENARIOS:
TC3: Unregistered email
  Steps: Enter email not in system
  Expected: Generic message "If email exists, reset link sent" (security)

TC4: Expired reset link (>24h)
  Steps: Use reset link after 24 hour period
  Expected: Error "Link expired. Request new reset link"

TC5: Weak password validation
  Test data:
    - "pass" (too short)
    - "password" (no uppercase/number)
    - "PASSWORD" (no lowercase/number)
    - "12345678" (no letters)
  Expected: Error message for each

EDGE CASES:
TC6: Multiple reset requests
  Steps: Request reset 5 times for same email
  Expected: Only latest link works, previous invalidated

TC7: Reset link reuse attempt
  Steps: Use same link twice
  Expected: Second attempt fails "Link already used"

TC8: Special characters in new password
  Test data: "P@ssw0rd!", "Пароль123"
  Expected: Accepted (if allowed) or clear validation error

SECURITY:
TC9: Brute force protection
  Steps: Attempt 10 password resets in 1 minute
  Expected: Rate limiting applied

TC10: CSRF token validation
  Steps: Attempt form submission without valid token
  Expected: Request rejected

Value proposition:

  • 10 comprehensive test cases за 30 секунд
  • Покрывает scenarios которые легко пропустить
  • Можно использовать как base и дополнить

Converting to automation code

Prompt:
---
Convert TC1 into Cypress test code.
Use Page Object pattern.
Add proper waits and assertions.
---

GPT-4 generates:

// pages/ForgotPasswordPage.js
class ForgotPasswordPage {
  visit() {
    cy.visit('/forgot-password');
  }

  enterEmail(email) {
    cy.get('[data-testid="email-input"]').type(email);
  }

  clickSendResetLink() {
    cy.get('[data-testid="send-reset-btn"]').click();
  }

  verifySuccessMessage() {
    cy.get('[data-testid="success-message"]')
      .should('be.visible')
      .and('contain', 'Reset link sent');
  }
}

// pages/ResetPasswordPage.js
class ResetPasswordPage {
  visitWithToken(token) {
    cy.visit(`/reset-password?token=${token}`);
  }

  enterNewPassword(password) {
    cy.get('[data-testid="new-password-input"]').type(password);
  }

  enterConfirmPassword(password) {
    cy.get('[data-testid="confirm-password-input"]').type(password);
  }

  submit() {
    cy.get('[data-testid="submit-btn"]').click();
  }

  verifyPasswordResetSuccess() {
    cy.get('[data-testid="success-message"]')
      .should('contain', 'Password reset successful');
  }
}

// tests/password-reset.spec.js
describe('Password Reset Flow', () => {
  const forgotPasswordPage = new ForgotPasswordPage();
  const resetPasswordPage = new ResetPasswordPage();
  const loginPage = new LoginPage();

  beforeEach(() => {
    // Setup: Create test user
    cy.task('createUser', {
      email: 'test@example.com',
      password: 'OldPass123'
    });
  });

  it('TC1: Valid password reset flow', () => {
    // Step 1-3: Request reset
    forgotPasswordPage.visit();
    forgotPasswordPage.enterEmail('test@example.com');
    forgotPasswordPage.clickSendResetLink();
    forgotPasswordPage.verifySuccessMessage();

    // Step 4-5: Get reset token from email
    cy.task('getLatestEmail', 'test@example.com').then((email) => {
      const resetToken = extractTokenFromEmail(email.body);

      // Step 6-8: Reset password
      resetPasswordPage.visitWithToken(resetToken);
      resetPasswordPage.enterNewPassword('NewPass123');
      resetPasswordPage.enterConfirmPassword('NewPass123');
      resetPasswordPage.submit();
      resetPasswordPage.verifyPasswordResetSuccess();

      // Verify: Can login with new password
      loginPage.visit();
      loginPage.login('test@example.com', 'NewPass123');
      loginPage.verifyLoginSuccess();

      // Verify: Old password doesn't work
      cy.clearCookies();
      loginPage.visit();
      loginPage.login('test@example.com', 'OldPass123');
      loginPage.verifyLoginError('Invalid credentials');
    });
  });
});

Качество кода:

  • ✅ Page Object pattern
  • ✅ Proper waits (should)
  • ✅ Data-testid selectors (best practice)
  • ✅ Comprehensive assertions
  • ⚠️ Может требовать minor adjustments

Code Review с AI

Automated code review

Use case: Pull request с новыми тестами

# Промпт для GPT-4
"""
Review this Selenium test code for issues:

```python
def test_login():
    driver.get("http://app.com/login")
    driver.find_element(By.ID, "user").send_keys("admin")
    driver.find_element(By.ID, "pass").send_keys("admin123")
    driver.find_element(By.ID, "btn").click()
    time.sleep(5)
    assert "Dashboard" in driver.page_source

Focus on:

  • Test reliability (flakiness potential)
  • Best practices violations
  • Maintainability issues """

**GPT-4 Review:**

```markdown
## Code Review Issues

### 🔴 Critical Issues

1. **Hard-coded sleep (line 5)**
   - Problem: `time.sleep(5)` causes flaky tests
   - Impact: Test may fail on slow systems or pass on fast ones
   - Fix:
   ```python
   from selenium.webdriver.support.ui import WebDriverWait
   from selenium.webdriver.support import expected_conditions as EC

   WebDriverWait(driver, 10).until(
       EC.presence_of_element_located((By.ID, "dashboard"))
   )
  1. Fragile assertion (line 6)
    • Problem: Checking entire page_source is unreliable
    • Impact: May break with whitespace/formatting changes
    • Fix:
    dashboard_element = driver.find_element(By.ID, "dashboard")
    assert dashboard_element.is_displayed()
    

🟡 Medium Issues

  1. Hard-coded URL

    • Problem: URL embedded in test
    • Fix: Use config/environment variables
    driver.get(f"{config.BASE_URL}/login")
    
  2. No error handling

    • Missing: What if login fails?
    • Add: Explicit wait for success/error message
  3. Weak selectors

    • By.ID, "btn" - very generic
    • Better: By.CSS_SELECTOR, "[data-testid='login-button']"

🟢 Suggestions

  1. Extract to Page Object

    class LoginPage:
        def __init__(self, driver):
            self.driver = driver
    
        def login(self, username, password):
            self.driver.get(f"{config.BASE_URL}/login")
            self.username_input.send_keys(username)
            self.password_input.send_keys(password)
            self.login_button.click()
    
        @property
        def username_input(self):
            return self.driver.find_element(By.ID, "user")
    
        # ... other elements
    
  2. Add test data externalization

    • Credentials should come from fixtures/config

Refactored Version

import pytest
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

@pytest.fixture
def login_page(driver):
    return LoginPage(driver)

@pytest.fixture
def dashboard_page(driver):
    return DashboardPage(driver)

def test_successful_login(login_page, dashboard_page, test_user):
    """
    Test successful login flow

    Given: Valid user credentials
    When: User submits login form
    Then: Dashboard page is displayed
    """
    login_page.open()
    login_page.login(test_user.username, test_user.password)

    # Explicit wait for dashboard
    assert dashboard_page.wait_until_loaded(), \
        "Dashboard did not load within timeout"

    # Verify user is logged in
    assert dashboard_page.get_username() == test_user.username

**Value:**
- Finds issues человеку легко пропустить
- Предлагает конкретные fixes
- Обучает best practices

### Test coverage analysis

Prompt:

Analyze this function and suggest missing test cases:

def calculate_shipping_cost(weight, distance, is_express, country):
    """Calculate shipping cost"""
    base_cost = weight * 0.5 + distance * 0.1

    if is_express:
        base_cost *= 1.5

    if country != "US":
        base_cost += 10  # International fee

    # Free shipping for orders over $100
    if base_cost > 100:
        return 0

    return round(base_cost, 2)

What test cases are needed to achieve 100% branch coverage?


**GPT-4 Analysis:**

```python
"""
Test Cases for Full Coverage:

1. Basic domestic shipping (non-express, US)
   calculate_shipping_cost(10, 100, False, "US")
   → Covers: base calculation, no express, US

2. Express domestic shipping
   calculate_shipping_cost(10, 100, True, "US")
   → Covers: express multiplier branch

3. International non-express
   calculate_shipping_cost(10, 100, False, "CA")
   → Covers: international fee branch

4. International express
   calculate_shipping_cost(10, 100, True, "UK")
   → Covers: both express AND international

5. Free shipping threshold (domestic)
   calculate_shipping_cost(150, 100, False, "US")
   → Covers: free shipping branch (>$100)

6. Free shipping threshold (international)
   calculate_shipping_cost(100, 150, True, "JP")
   → Covers: free shipping even with international fee

7. Edge case: Zero weight/distance
   calculate_shipping_cost(0, 0, False, "US")
   → Edge case validation

8. Edge case: Exactly $100
   calculate_shipping_cost(weight=X, distance=Y, ...)
   where result = exactly 100
   → Boundary testing

MISSING VALIDATION (potential bugs):
- Negative weight/distance?
- Invalid country code?
- None values?

Suggested additional tests:
```python
@pytest.mark.parametrize("weight,distance,is_express,country,expected", [
    (10, 100, False, "US", 15.0),     # TC1
    (10, 100, True, "US", 22.5),      # TC2
    (10, 100, False, "CA", 25.0),     # TC3
    (10, 100, True, "UK", 32.5),      # TC4
    (150, 100, False, "US", 0),       # TC5 - free shipping
    (100, 150, True, "JP", 0),        # TC6 - free international
])
def test_shipping_cost_calculation(weight, distance, is_express, country, expected):
    result = calculate_shipping_cost(weight, distance, is_express, country)
    assert result == expected

# Edge cases
def test_zero_weight_and_distance():
    assert calculate_shipping_cost(0, 0, False, "US") == 0

def test_exactly_100_threshold():
    # Find combination that yields exactly $100
    result = calculate_shipping_cost(166.67, 33.33, False, "US")
    assert result == 0  # Should trigger free shipping

# Error cases (if validation added)
def test_negative_weight_raises_error():
    with pytest.raises(ValueError):
        calculate_shipping_cost(-10, 100, False, "US")

"""


## Риски и ограничения

### Hallucinations

**Проблема #1:** LLM "придумывает" несуществующие API

```python
# Промпт: "Generate Cypress test for file upload"

# GPT может сгенерировать:
cy.uploadFile('test.pdf')  // ❌ Этого метода не существует!

# Правильно:
cy.get('input[type="file"]').selectFile('test.pdf')  // ✅

Почему это опасно:

  • Код выглядит правдоподобно
  • Junior QA может не заметить
  • Waste времени на debugging

Митигация:

  • Всегда проверяй сгенерированный код
  • Используй IDE с autocomplete для валидации
  • Code review обязателен

Outdated knowledge

Проблема #2: Cutoff date знаний

GPT-4 обучен на данных до Apr 2023. Новые фреймворки/библиотеки он не знает.

# Промпт: "Generate Playwright test"

# GPT может использовать старый синтаксис:
await page.click('#button')  // Deprecated

# Новый синтаксис (2024):
await page.locator('#button').click()  // Current best practice

Митигация:

  • Указывай версии в промпте: “Generate Playwright 1.40 test”
  • Проверяй против актуальной документации
  • Используй plugins с актуальными данными (если доступны)

Security risks

Проблема #3: Утечка чувствительных данных

# ❌ НИКОГДА не делай так:
prompt = f"""
Review this code:
{code_with_api_keys}  # Отправляешь секреты в OpenAI!
"""

Best practices:

  • Sanitize код перед отправкой в LLM
  • Используй local LLM для sensitive code (Llama 3)
  • Redact credentials/secrets
# ✅ Правильно:
import re

def sanitize_code(code):
    # Remove API keys
    code = re.sub(r'api_key\s*=\s*["\'][^"\']+["\']', 'api_key="REDACTED"', code)
    # Remove passwords
    code = re.sub(r'password\s*=\s*["\'][^"\']+["\']', 'password="REDACTED"', code)
    return code

clean_code = sanitize_code(original_code)
# Теперь безопасно отправлять в LLM

Quality consistency

Проблема #4: Качество варьируется

Same prompt → разные результаты из-за temperature parameter.

# Temperature = 0.0 → Детерминированно (одинаковый output)
# Temperature = 0.7 → Креативно (разный output)
# Temperature = 1.0+ → Хаотично

Для тестов:

  • Используй temperature=0 для consistency
  • Проверяй результаты несколько раз
  • Не доверяй вслепую

Over-reliance danger

Проблема #5: “AI написал тест, значит он правильный”

# AI сгенерировал тест
def test_user_registration():
    response = api.register(email="test@test.com", password="pass")
    assert response.status_code == 200  # ❌ Недостаточно!

Что пропущено:

  • Проверка что user создан в БД
  • Email verification отправлен
  • Password правильно хэширован
  • Нет дубликатов

Правило: AI - это помощник, не замена критическому мышлению

Best Practices использования LLM в QA

1. Effective prompting

Bad prompt:

Generate tests for login

Good prompt:

Generate Cypress tests for login functionality.

Context:
- App: E-commerce site
- Framework: Cypress 13.x
- Pattern: Page Object Model
- Authentication: JWT tokens

Requirements:
- Cover positive and negative scenarios
- Include edge cases (special chars in password, etc)
- Add proper waits (no hard-coded sleeps)
- Use data-testid selectors
- Add clear comments

Output: Complete test file with imports and fixtures

Результат: Значительно лучшее качество кода

2. Iterative refinement

User: Generate API test for user registration

GPT: [generates basic test]

User: Add validation for:
- Email format
- Password strength requirements (8+ chars, 1 uppercase, 1 number)
- Duplicate email handling

GPT: [refines test]

User: Convert to pytest with fixtures and parametrize for multiple test data

GPT: [final version]

Каждая итерация улучшает результат.

3. Use LLM as learning tool

Prompt: Explain what this test code does, line by line:

[paste complex test]

Then suggest improvements and explain why they're better.

Value: Обучение + code review в одном

4. Domain-specific fine-tuning

Для enterprise: Fine-tune модель на ваших тестах

# Обучи GPT на ваших best practices
training_data = [
    {
        "prompt": "Generate login test",
        "completion": "[ваш идеальный login test template]"
    },
    # ... 100+ примеров
]

# Fine-tuned model будет генерировать в вашем стиле

5. Human-in-the-loop

Workflow:
1. LLM генерирует тест → Draft
2. QA reviewer → Adjusts & approves
3. CI/CD runs test → Validates
4. Feedback loop → Improves prompts

Never fully automated — всегда human review.

Реальные кейсы использования

Case 1: Regression test suite generation

Компания: E-commerce SaaS (500K LOC)

Задача: Legacy code без тестов, нужно 80% coverage

Решение:

  1. Извлекли список всех API endpoints
  2. Для каждого endpoint → GPT-4 prompt:
    Generate comprehensive API tests for:
    POST /api/orders
    
    [Include Swagger spec]
    
    Cover: CRUD operations, validation, auth, edge cases
    
  3. Generated 2,300 тестов за 2 дня
  4. Human review + fixes → 1 неделя
  5. Final: 1,800 working tests (78% автоматически сгенерированных)

ROI:

  • Ручное написание: ~6 месяцев
  • С GPT-4: 2 недели
  • Экономия: ~$120K

Case 2: Test data generation для ML

Компания: Fintech (fraud detection ML)

Задача: Нужны realistic fraudulent transaction patterns

Решение:

prompt = """
Generate 100 realistic credit card transaction records.
Include 20 fraudulent patterns:
- Card testing (multiple small charges)
- Account takeover (sudden large purchases)
- Geographic anomalies (purchases in 2 countries within hours)
- Unusual merchant categories

Make legitimate transactions realistic too.
Output: CSV
"""

Результат:

  • ML model обучилась распознавать более subtle patterns
  • Precision увеличилась на 12%
  • Ложные срабатывания снизились на 8%

Case 3: Documentation generation

Компания: Healthcare SaaS

Задача: Документация по 300 тест-кейсам устарела

Решение:

for test_file in test_files:
    code = read_file(test_file)

    prompt = f"""
    Generate user-friendly test documentation:

    {code}

    Format:
    - Test ID
    - Purpose
    - Prerequisites
    - Steps
    - Expected results
    - Related requirements
    """

    documentation = llm.predict(prompt)
    save_to_wiki(documentation)

Результат:

  • 300 тест-кейсов документированы за 4 часа
  • Human review + corrections: 1 день
  • Вместо 2-3 недель ручной работы

Будущее LLM в тестировании

1. Specialized QA LLMs:

  • Модели обученные специально на QA данных
  • Лучшее понимание test patterns
  • Меньше hallucinations для testing tasks

2. Agentic workflows:

# AI agent самостоятельно:
1. Analyze requirements
2. Generate tests
3. Run tests
4. Analyze failures
5. Fix flaky tests
6. Report results

# Human только approves/guides

3. Multi-modal testing:

  • LLM + Computer Vision для UI testing
  • “Посмотри на screenshot и скажи что сломалось”

4. Real-time test generation:

# Во время exploratory testing:
QA действие  LLM генерирует test  Auto-adds to suite

# Превращает manual testing в automated

Заключение

ChatGPT и LLM — это мощные инструменты для QA, но не серебряная пуля.

Где LLM действительно полезны:

✅ Генерация тест-данных (90% времени экономится)

✅ Создание базовых тест-кейсов (70% быстрее)

✅ Code review (находит 60-70% очевидных проблем)

✅ Documentation generation (95% автоматизация)

✅ Learning & upskilling (бесконечный mentor)

Где LLM НЕ заменяют человека:

❌ Critical thinking (edge cases требуют domain knowledge)

❌ Test strategy (что тестировать и почему)

❌ Bug investigation (root cause analysis)

❌ Context understanding (бизнес-специфика)

Golden Rule:

LLM - это супер-умный junior QA. Генерирует быстро, но требует supervision. Не доверяй вслепую. Всегда verify.

Практические рекомендации:

  1. Start small: Используй для test data generation
  2. Build prompts library: Сохраняй успешные промпты
  3. Set up guardrails: Sanitization, review process
  4. Measure impact: Track time saved, quality metrics
  5. Train team: Не все умеют эффективно promptить

LLM в тестировании — это будущее, которое уже наступило. Вопрос не “использовать ли”, а “как использовать эффективно и безопасно”.