← Назад к вопросам

Как тестировал интервал анализа граничных значений

1.3 Junior🔥 231 комментариев
#Теория тестирования#Техники тест-дизайна

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как тестировал интервал анализа граничных значений

Boundary Value Analysis (BVA) — это техника тестирования, которая фокусируется на значениях на границах диапазонов, так как именно там часто находятся ошибки. Я расскажу, как я практически использую эту методику.

Суть Boundary Value Analysis

Большинство ошибок в коде находятся на границах интервалов. Например:

  • Проверка age > 18 часто неправильно реализуется как age >= 18
  • Проверка размера массива length < 100 может быть ошибочно length <= 100

Если я тестирую возраст (должен быть от 18 до 65), я тестирую не просто случайные числа, а именно граничные значения.

Практический пример: интервал 18-65 лет

Определяю границы:

  • Минимальная граница: 18 (включена)
  • Максимальная граница: 65 (включена)
  • Диапазон: [18, 65]

Граничные значения для тестирования:

ТестЗначениеОжидаемый результатТип ошибки, которую ловит
Ниже минимума17Отклонено (invalid)Ошибка в условии > вместо >=
На минимуме18Принято (valid)Правильно ли включена граница
Выше минимума19Принято (valid)Не сломана ли валидация
Ниже максимума64Принято (valid)Не сломана ли валидация
На максимуме65Принято (valid)Правильно ли включена граница
Выше максимума66Отклонено (invalid)Ошибка в условии < вместо <=

Как я структурирую тесты

Approach 1: Отдельный тест на каждое значение

import pytest

def validate_age(age: int) -> bool:
    """Возвращает True если возраст в диапазоне [18, 65]"""
    return 18 <= age <= 65

class TestAgeBoundaries:
    """Тесты граничных значений для валидации возраста"""
    
    # Ниже минимума
    def test_age_below_minimum(self):
        """Возраст 17 (на 1 ниже граници) должен быть отклонен"""
        assert validate_age(17) == False
    
    # На минимуме
    def test_age_at_minimum_boundary(self):
        """Возраст 18 (точно на границе) должен быть принят"""
        assert validate_age(18) == True
    
    # Выше минимума (близко к границе)
    def test_age_just_above_minimum(self):
        """Возраст 19 (на 1 выше границы) должен быть принят"""
        assert validate_age(19) == True
    
    # Ниже максимума
    def test_age_just_below_maximum(self):
        """Возраст 64 (на 1 ниже границы) должен быть принят"""
        assert validate_age(64) == True
    
    # На максимуме
    def test_age_at_maximum_boundary(self):
        """Возраст 65 (точно на границе) должен быть принят"""
        assert validate_age(65) == True
    
    # Выше максимума
    def test_age_above_maximum(self):
        """Возраст 66 (на 1 выше границы) должен быть отклонен"""
        assert validate_age(66) == False

Approach 2: Параметризированные тесты (более эффективно)

import pytest

class TestAgeBoundariesParametrized:
    
    @pytest.mark.parametrize("age,expected", [
        (17, False),   # Ниже минимума
        (18, True),    # На минимуме
        (19, True),    # Выше минимума
        (64, True),    # Ниже максимума
        (65, True),    # На максимуме
        (66, False),   # Выше максимума
    ])
    def test_age_validation(self, age: int, expected: bool):
        """Тестирует все граничные значения в одном параметризированном тесте"""
        assert validate_age(age) == expected

Более сложный пример: диапазон доходов

Тестирую систему налогообложения, где налог зависит от дохода:

  • 0-25 000 → 13%
  • 25 001-50 000 → 20%
  • 50 001-100 000 → 30%
  • 100 001+ → 35%

Граничные значения:

def calculate_tax_rate(income: int) -> float:
    if income <= 25_000:
        return 0.13
    elif income <= 50_000:
        return 0.20
    elif income <= 100_000:
        return 0.30
    else:
        return 0.35

class TestTaxBoundaries:
    
    @pytest.mark.parametrize("income,expected_rate", [
        # Диапазон 1: [0, 25_000]
        (24_999, 0.13),      # Ниже первой границы
        (25_000, 0.13),      # На первой границе
        (25_001, 0.20),      # Переход на вторую ставку
        
        # Диапазон 2: (25_000, 50_000]
        (49_999, 0.20),      # Ниже второй границы
        (50_000, 0.20),      # На второй границе
        (50_001, 0.30),      # Переход на третью ставку
        
        # Диапазон 3: (50_000, 100_000]
        (99_999, 0.30),      # Ниже третьей границы
        (100_000, 0.30),     # На третьей границе
        (100_001, 0.35),     # Переход на четвёртую ставку
    ])
    def test_tax_rate_calculation(self, income: int, expected_rate: float):
        assert calculate_tax_rate(income) == expected_rate

Реальный пример: API тестирование с BVA

Тестирую endpoint для создания заказа, где минимальное количество товаров = 1, максимальное = 1000.

import pytest
from decimal import Decimal
from api_client import create_order

class TestOrderQuantityBoundaries:
    """Граничные тесты для количества товаров в заказе"""
    
    @pytest.mark.parametrize("quantity,expected_status", [
        (0, 400),          # Ниже минимума — ошибка
        (1, 201),          # На минимуме — успех
        (2, 201),          # Выше минимума — успех
        (999, 201),        # Ниже максимума — успех
        (1000, 201),       # На максимуме — успех
        (1001, 400),       # Выше максимума — ошибка
    ])
    def test_order_creation_with_boundary_quantities(self, quantity: int, expected_status: int):
        response = create_order(
            product_id="123",
            quantity=quantity,
            user_id="user_456"
        )
        assert response.status_code == expected_status
    
    def test_order_quantity_validation_message(self):
        """Проверяю, что сообщение об ошибке правильное"""
        response = create_order(
            product_id="123",
            quantity=0,
            user_id="user_456"
        )
        assert response.status_code == 400
        assert "Minimum quantity is 1" in response.json()["error"]

Комбинированные граничные тесты (Multiple Parameters)

Если функция зависит от нескольких параметров, я тестирую их комбинации на границах:

def calculate_shipping_cost(weight: int, distance: int) -> Decimal:
    """
    weight: 1-100 кг
    distance: 1-10000 км
    """
    base_cost = Decimal(10)
    weight_cost = Decimal(weight) * Decimal(0.5)
    distance_cost = Decimal(distance) * Decimal(0.01)
    return base_cost + weight_cost + distance_cost

class TestShippingCostBoundaries:
    
    @pytest.mark.parametrize("weight,distance,scenario", [
        # Минимальные значения
        (1, 1, "min_weight_min_distance"),
        # Максимальные значения
        (100, 10000, "max_weight_max_distance"),
        # Смешанные
        (1, 10000, "min_weight_max_distance"),
        (100, 1, "max_weight_min_distance"),
        # На границе
        (1, 5000, "min_weight_mid_distance"),
        (50, 1, "mid_weight_min_distance"),
    ])
    def test_shipping_cost_boundaries(self, weight: int, distance: int, scenario: str):
        cost = calculate_shipping_cost(weight, distance)
        assert cost >= Decimal(10)  # Минимальная цена
        # Дополнительные проверки по сценарию

Как я документирую BVA тесты

"""
Boundary Value Analysis для функции validate_age(age: int) -> bool

Диапазон: [18, 65]

Тестовые точки:
1. 17 (age = min - 1) → False
2. 18 (age = min) → True
3. 19 (age = min + 1) → True
4. 64 (age = max - 1) → True
5. 65 (age = max) → True
6. 66 (age = max + 1) → False

Ожидаемые ошибки, которые ловят эти тесты:
- Off-by-one errors (age > 18 вместо age >= 18)
- Неправильные операторы (< вместо <=)
- Copy-paste ошибки в условиях
"""

Инструменты для автоматизации BVA

# Property-based testing (автоматически генерирует граничные случаи)
pip install hypothesis
from hypothesis import given, strategies as st

class TestAgeWithHypothesis:
    
    @given(st.integers(min_value=18, max_value=65))
    def test_valid_ages(self, age):
        assert validate_age(age) == True
    
    @given(st.integers(max_value=17))
    def test_invalid_ages_below(self, age):
        assert validate_age(age) == False
    
    @given(st.integers(min_value=66))
    def test_invalid_ages_above(self, age):
        assert validate_age(age) == False

Мой чеклист для BVA тестирования

  1. ✅ Определить все диапазоны значений
  2. ✅ Найти все граничные точки (min, max)
  3. ✅ Создать тест на каждую граничную точку (n-1, n, n+1)
  4. ✅ Определить эквивалентные классы значений
  5. ✅ Протестировать комбинации если параметров несколько
  6. ✅ Проверить error messages на граничных значениях
  7. ✅ Использовать параметризированные тесты для масштабируемости
  8. ✅ Документировать BVA стратегию
  9. ✅ Убедиться, что тесты ловят off-by-one ошибки
  10. ✅ Запустить тесты на разных окружениях

Boundary Value Analysis — это один из самых эффективных методов тестирования, потому что большинство ошибок находятся именно на границах. Опытный QA всегда применяет эту технику при тестировании любого функционала с числовыми или диапазонными параметрами.