White-box Testing (White-box Testing)

Тест знает о внутренней реализации системы: лезет напрямую в базу данных, вызывает приватные методы через рефлексию или проверяет SQL-запросы вместо поведения с точки зрения пользователя.

Симптомы

  • jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE ...") в тесте UI/API уровня
  • ReflectionUtils.invokeMethod(privateMethod, service, args) для проверки приватной логики
  • Тест верифицирует структуру БД вместо пользовательского поведения
  • Mock-объекты проверяют, что был вызван внутренний метод, а не что вернулся правильный результат
  • Тест «ломается» при переименовании приватного метода или изменении схемы БД

Причины возникновения

  • Желание убедиться, что данные «реально сохранились в БД», а не доверять UI
  • Недоверие к тестируемой системе: «а вдруг UI врёт»
  • Смешение уровней тестирования: в UI-тест вносится логика интеграционного теста
  • Отсутствие разграничения ответственности между слоями тест-пирамиды

Почему это проблема

  • Хрупкость к рефакторингу: изменили схему БД или переименовали метод — тест сломался, хотя поведение то же
  • Нарушение уровней абстракции: UI-тест не должен знать о SQL и структуре таблиц
  • Тест проверяет реализацию, а не контракт: потеряла смысл гарантия, что «система работает для пользователя»
  • Сложная поддержка: нужно следить сразу за поведением и за реализацией

Решение

Проверять через тот же интерфейс, что использует пользователь:

// ❌ прямой SQL в UI-тесте
int count = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM users WHERE email = 'new@user.com'", Integer.class);
assertEquals(1, count);

// ✅ проверка через UI/API — тот же уровень абстракции
DashboardPage dashboard = loginPage.login("new@user.com", "Pass123!");
assertThat(dashboard.getGreeting()).isNotBlank();