Тестирование чёрного ящика — это фундаментальный подход к тестированию, при котором тестировщик проверяет функциональность приложения, не зная его внутренней структуры кода, деталей реализации или внутренних путей. Фокус полностью на входных данных и ожидаемых выходных данных, рассматривая программное обеспечение как “чёрный ящик”.
Этот подход является основой техник проектирования тест-кейсов и тесно связан с исследовательским тестированием. Понимание тестирования чёрного ящика критично важно для разработки эффективной стратегии автоматизации тестирования и качественного документирования дефектов.
Что такое Тестирование Чёрного Ящика?
Тестирование чёрного ящика, также известное как поведенческое тестирование или тестирование на основе спецификаций, проверяет функциональность программного обеспечения в соответствии с требованиями и спецификациями. Тестировщикам не нужны знания программирования или доступ к исходному коду—им нужно только понимать, что должна делать система.
Ключевые Характеристики
- Основано на спецификациях: Тесты выводятся из требований, пользовательских историй или спецификаций
- Без доступа к коду: Тестировщики не исследуют внутреннюю структуру кода
- Перспектива конечного пользователя: Тесты имитируют реальное поведение и сценарии пользователя
- Фокус на функциональности: Проверяет, работают ли функции как задумано
- Независимость от реализации: Тесты остаются действительными, даже если внутренняя реализация меняется
Когда Использовать Тестирование Чёрного Ящика
Тестирование чёрного ящика идеально для:
- Функционального тестирования: Проверка функций в соответствии с требованиями
- Системного тестирования: Тестирование полной интегрированной системы
- Приёмочного тестирования: Проверка соответствия программного обеспечения потребностям бизнеса
- Регрессионного тестирования: Обеспечение того, что изменения не нарушают существующую функциональность
- Бета/UAT тестирования: Реальные пользователи тестируют в средах, похожих на продакшн
Основные Техники Тестирования Чёрного Ящика
1. Разбиение на Классы Эквивалентности
Разбиение на классы эквивалентности делит входные данные на валидные и невалидные классы, где все условия в классе ведут себя одинаково. Вместо тестирования всех возможных входных данных, вы тестируете одно представительное значение из каждого класса.
Пример: Проверка возраста для заявки на страхование
# Входные данные: Возраст должен быть между 18-65 для допуска
# Классы эквивалентности:
# Невалидный: возраст < 18
# Валидный: 18 ≤ возраст ≤ 65
# Невалидный: возраст > 65
# Тестовые случаи (один из каждого класса):
test_cases = [
(15, False), # Ниже минимума
(30, True), # Валидный диапазон
(70, False) # Выше максимума
]
def test_age_eligibility():
for age, expected in test_cases:
result = check_eligibility(age)
assert result == expected, f"Не удалось для возраста {age}"
2. Анализ Граничных Значений (АГЗ)
Анализ граничных значений фокусируется на тестировании на границах классов эквивалентности, где часто возникают дефекты. Тестируйте значения на границах, чуть ниже и чуть выше.
Пример: Проверка длины пароля
# Требование: Пароль должен быть 8-20 символов
# Тестовые значения:
boundary_tests = [
("1234567", False), # Длина 7 (чуть ниже минимума)
("12345678", True), # Длина 8 (минимальная граница)
("123456789", True), # Длина 9 (чуть выше минимума)
("12345678901234567890", True), # Длина 20 (максимальная граница)
("123456789012345678901", False) # Длина 21 (чуть выше максимума)
]
def test_password_length():
for password, expected in boundary_tests:
result = validate_password(password)
assert result == expected, f"Не удалось для длины {len(password)}"
3. Тестирование Таблицы Решений
Таблицы решений отображают комбинации входных данных на ожидаемые действия или выходные данные. Они идеальны для тестирования сложной бизнес-логики с множественными условиями.
Пример: Система одобрения кредита
| Кредитный Рейтинг | Доход | Занятость | Кредит Одобрен |
|---|---|---|---|
| Высокий (>700) | Высокий | Стабильная | ✓ Да |
| Высокий | Низкий | Стабильная | ✓ Да |
| Высокий | Высокий | Нестабильная | ✓ Да |
| Высокий | Низкий | Нестабильная | ✗ Нет |
| Низкий (<700) | Высокий | Стабильная | ✗ Нет |
| Низкий | Низкий | Стабильная | ✗ Нет |
| Низкий | Высокий | Нестабильная | ✗ Нет |
| Низкий | Низкий | Нестабильная | ✗ Нет |
def test_loan_approval():
test_scenarios = [
# (кредитный_рейтинг, доход, занятость, ожидаемое)
(750, "высокий", "стабильная", True),
(750, "низкий", "стабильная", True),
(750, "высокий", "нестабильная", True),
(750, "низкий", "нестабильная", False),
(650, "высокий", "стабильная", False),
(650, "низкий", "стабильная", False),
(650, "высокий", "нестабильная", False),
(650, "низкий", "нестабильная", False),
]
for score, income, employment, expected in test_scenarios:
result = approve_loan(score, income, employment)
assert result == expected
4. Тестирование Переходов Состояний
Тестирование переходов состояний проверяет, как система ведёт себя при переходе между различными состояниями на основе событий или входных данных.
Пример: Система обработки заказов
Состояния: Новый → Оплачен → Отправлен → Доставлен → Завершён
↓ ↓ ↓ ↓
Отменён (из любого состояния)
class OrderState:
NEW = "новый"
PAID = "оплачен"
SHIPPED = "отправлен"
DELIVERED = "доставлен"
COMPLETED = "завершён"
CANCELLED = "отменён"
def test_order_state_transitions():
# Валидные переходы
assert process_payment("новый") == "оплачен"
assert ship_order("оплачен") == "отправлен"
assert deliver_order("отправлен") == "доставлен"
assert complete_order("доставлен") == "завершён"
# Невалидные переходы
with pytest.raises(InvalidTransition):
ship_order("новый") # Нельзя отправить неоплаченный заказ
# Отмена из любого состояния
for state in [OrderState.NEW, OrderState.PAID, OrderState.SHIPPED]:
assert cancel_order(state) == OrderState.CANCELLED
5. Тестирование Сценариев Использования
Тестирование сценариев использования выводит тестовые случаи из use case, описывающих взаимодействия пользователь-система. Каждый use case включает основной поток, альтернативные потоки и потоки исключений.
Пример: Use case входа в систему
Функция: Вход пользователя в систему
Сценарий: Успешный вход с валидными учётными данными
Дано пользователь находится на странице входа
Когда пользователь вводит валидное имя пользователя "ivan@example.com"
И пользователь вводит валидный пароль "БезопасныйПароль123"
И пользователь нажимает кнопку входа
Тогда пользователь должен быть перенаправлен на панель управления
И должно отображаться приветственное сообщение "Добро пожаловать, Иван"
Сценарий: Неудачный вход с невалидным паролем
Дано пользователь находится на странице входа
Когда пользователь вводит валидное имя пользователя "ivan@example.com"
И пользователь вводит невалидный пароль "НеправильныйПароль"
И пользователь нажимает кнопку входа
Тогда должно отображаться сообщение об ошибке "Неверные учётные данные"
И пользователь должен остаться на странице входа
Сценарий: Блокировка учётной записи после неудачных попыток
Дано пользователь неудачно пытался войти 2 раза
Когда пользователь вводит неверные учётные данные снова
Тогда учётная запись должна быть заблокирована
И должно отображаться сообщение об ошибке "Учётная запись заблокирована"
6. Тестирование All-Pairs (Попарное)
Тестирование all-pairs сокращает количество комбинаций тестов при сохранении покрытия. Оно обеспечивает тестирование каждой пары входных параметров вместе хотя бы один раз.
Пример: Тестирование совместимости браузеров
# Параметры:
# Браузер: Chrome, Firefox, Safari
# ОС: Windows, macOS, Linux
# Разрешение: 1920x1080, 1366x768
# Полная комбинация: 3 × 3 × 2 = 18 тестов
# Сокращение all-pairs: ~9 тестов
pairwise_combinations = [
("Chrome", "Windows", "1920x1080"),
("Chrome", "macOS", "1366x768"),
("Chrome", "Linux", "1920x1080"),
("Firefox", "Windows", "1366x768"),
("Firefox", "macOS", "1920x1080"),
("Firefox", "Linux", "1366x768"),
("Safari", "Windows", "1920x1080"),
("Safari", "macOS", "1366x768"),
("Safari", "Linux", "1920x1080"),
]
def test_browser_compatibility():
for browser, os, resolution in pairwise_combinations:
launch_browser(browser, os, resolution)
assert page_loads_correctly()
assert elements_render_properly()
Продвинутые Техники Чёрного Ящика
Угадывание Ошибок
Угадывание ошибок опирается на опыт тестировщика для предвидения, где могут возникнуть дефекты. Тестировщики используют интуицию и прошлый опыт для выявления проблемных областей.
Типичные сценарии, подверженные ошибкам:
# Типичные входные данные, подверженные ошибкам для тестирования
error_prone_tests = [
# Специальные символы
test_input("'; DROP TABLE users--"), # SQL-инъекция
test_input("<script>alert('XSS')</script>"), # XSS
# Граничные случаи
test_input(""), # Пустой ввод
test_input(" " * 1000), # Очень длинный ввод
test_input("null"), # Null значения
# Проблемы формата
test_input("0000-00-00"), # Невалидная дата
test_input("999-999-9999"), # Невалидный телефон
test_input("неемейл"), # Невалидный email
]
Исследовательское Тестирование
Исследовательское тестирование — это одновременное обучение, проектирование тестов и выполнение. Тестировщики свободно исследуют приложение, используя креативность и интуицию для поиска дефектов.
Сессия с ограничением по времени:
СЕССИЯ: Корзина покупок - 60 минут
МИССИЯ: Исследовать функциональность корзины с фокусом на крайние случаи
ОБЛАСТИ:
- Добавление/удаление товаров
- Обновление количества
- Расчёт цен
- Промо-коды
- Сохранение сессии
НАХОДКИ:
1. Корзина не обновляется при удалении последнего товара
2. Отрицательные количества принимаются через API
3. Просроченные промо-коды применяются успешно
4. Корзина очищается после таймаута сессии без предупреждения
Лучшие Практики Тестирования Чёрного Ящика
1. Начинайте с Требований
Всегда основывайте тесты на задокументированных требованиях, пользовательских историях или спецификациях. Чёткие требования ведут к эффективным тестовым случаям.
Пользовательская История: Как клиент, я хочу искать товары по названию
Критерии Приёмки:
- Поиск возвращает результаты, содержащие поисковый термин
- Поиск не чувствителен к регистру
- Результаты показываются в течение 2 секунд
- Максимум 20 результатов на странице
- Пустой поиск возвращает все товары
Тестовые Случаи:
- TC001: Поиск с точным названием товара
- TC002: Поиск с частичным названием
- TC003: Поиск со смешанным регистром
- TC004: Проверка времени отклика < 2с
- TC005: Проверка пагинации на 20 товарах
- TC006: Проверка поведения пустого поиска
2. Комбинируйте Техники
Используйте несколько техник вместе для всесторонн ого покрытия:
def test_registration_form():
# Разбиение на Классы Эквивалентности + АГЗ
test_email_formats()
test_password_strength()
# Таблица Решений
test_field_combinations()
# Переход Состояний
test_form_submission_states()
# Угадывание Ошибок
test_special_characters()
test_sql_injection_attempts()
3. Приоритизируйте Тестовые Случаи
Не все тесты одинаково важны. Приоритизируйте на основе:
- Риск: Высокорисковые функции нуждаются в большем тестировании
- Использование: Часто используемые функции критичны
- Сложность: Сложная логика нуждается в тщательном тестировании
- Влияние на бизнес: Критичные для дохода функции идут первыми
# Высокий Приоритет
@pytest.mark.priority("высокий")
def test_payment_processing():
"""Критический путь - влияние на доход"""
pass
# Средний Приоритет
@pytest.mark.priority("средний")
def test_search_functionality() (как обсуждается в [Bug Anatomy: From Discovery to Resolution](/blog/bug-anatomy)):
"""Часто используется - пользовательский опыт"""
pass
# Низкий Приоритет
@pytest.mark.priority("низкий")
def test_footer_links():
"""Низкий риск - минимальное влияние"""
pass
4. Чётко Документируйте Тестовые Случаи
def test_user_registration_with_valid_data():
"""
ID Теста: TC_REG_001
Описание: Проверить, что пользователь может зарегистрироваться с валидными данными
Предусловия:
- Приложение доступно
- Email ещё не зарегистрирован
Шаги Теста:
1. Перейти на страницу регистрации
2. Ввести валидный email
3. Ввести валидный пароль (8+ символов)
4. Нажать кнопку Регистрация
Ожидаемый Результат:
- Регистрация успешна
- Пользователь перенаправлен на панель управления
- Приветственное письмо отправлено
Тестовые Данные:
- Email: новыйпользователь@example.com
- Пароль: ВалидныйПароль123
"""
# Реализация теста
pass
Реальный Пример Тестирования Чёрного Ящика
Процесс Оформления Заказа в E-commerce
class TestCheckoutFlow:
"""Полный набор тестов чёрного ящика для checkout"""
def test_guest_checkout_valid_card(self):
"""Счастливый путь - гостевой пользователь, валидный платёж"""
# Добавить товары в корзину
add_to_cart("Товар A", quantity=2)
add_to_cart("Товар B", quantity=1)
# Перейти к оформлению заказа
goto_checkout()
# Ввести информацию о доставке
fill_shipping({
"name": "Иван Иванов",
"address": "ул. Главная 123",
"city": "Москва",
"zip": "101000"
})
# Ввести платёжные данные
fill_payment({
"card_number": "4111111111111111",
"expiry": "12/25",
"cvv": "123"
})
# Отправить заказ
submit_order()
# Проверить
assert order_confirmed()
assert confirmation_email_sent()
assert inventory_updated()
def test_checkout_with_invalid_card(self):
"""Путь ошибки - невалидный способ оплаты"""
add_to_cart("Товар A")
goto_checkout()
fill_shipping(valid_shipping_data())
fill_payment({
"card_number": "4111111111111112", # Невалидная
"expiry": "12/25",
"cvv": "123"
})
submit_order()
# Проверить обработку ошибок
assert error_displayed("Платёж отклонён")
assert order_not_created()
assert cart_preserved()
def test_checkout_with_expired_coupon(self):
"""Крайний случай - просроченный промо-код"""
add_to_cart("Товар A")
goto_checkout()
apply_coupon("ПРОСРОЧЕН2024")
assert error_displayed("Купон просрочен")
assert discount_not_applied()
def test_checkout_performance(self):
"""Нефункциональный - время отклика"""
start = time.time()
complete_checkout_flow()
duration = time.time() - start
assert duration < 5.0, "Checkout занял слишком много времени"
Преимущества и Ограничения
Преимущества
- Не требуются знания программирования: QA команда может фокусироваться на функциональности
- Независимая перспектива: Тестировщики видят ПО как пользователи
- Широкое покрытие: Тестирует все функциональные аспекты
- Переиспользуемые тесты: Тесты выживают при изменении реализации
Ограничения
- Ограниченное покрытие кода: Может пропустить ошибки внутренней логики
- Не может тестировать алгоритмы: Внутренние вычисления не проверяются
- Пробелы покрытия путей: Не все пути кода выполняются
- Позднее обнаружение дефектов: Проблемы находятся на поздних стадиях тестирования
Заключение
Тестирование чёрного ящика необходимо для проверки программного обеспечения с точки зрения пользователя. Овладев техниками проектирования тест-кейсов, такими как разбиение на классы эквивалентности, анализ граничных значений, таблицы решений и тестирование переходов состояний, вы можете создавать всесторонние наборы тестов, обеспечивающие соответствие программного обеспечения требованиям и ожидаемой функциональности.
Ключ к успешному тестированию чёрного ящика — комбинирование нескольких техник, эффективная приоритизация и поддержание чёткой документации. Независимо от того, тестируете ли вы простую форму или сложную корпоративную систему, тестирование чёрного ящика обеспечивает основу для обеспечения качества.
Смотрите также
- Техники проектирования тест-кейсов — систематические методы создания эффективных тестов
- Исследовательское тестирование: полное руководство — неструктурированный подход к тестированию
- Анализ граничных значений — детальное изучение техники BVA
- Баг-репорты, которые любят разработчики — как документировать найденные дефекты
- Стратегия автоматизации тестирования — автоматизация техник чёрного ящика