Книга: Экстремальное программирование. Разработка через тестирование

Проверка утверждений

Проверка утверждений

Как убедиться в том, что тест работает правильно? Напишите логическое выражение, которое автоматически подтвердит ваше мнение о том, что код работает.

Если мы хотим сделать тесты полностью автоматическими, значит, абсолютно все предположения о работе тестируемого кода необходимо превратить в тесты, при этом результат выполнения этих тестов должен говорить нам, работает код корректно или нет. Проще говоря, мы должны обладать возможностью щелкнуть на кнопке и через короткое время узнать, работает код корректно или нет. Отсюда следует, что

• в результате выполнения теста должно получиться логическое значение: «истина» (True) указывает, что все в порядке, а «ложь» – что произошло нечто непредвиденное;

• проверка результата каждого теста выполняется компьютером автоматически при помощи какой-либо разновидности оператора assert().

Мне приходилось видеть выражения наподобие assertTrue(rectangle.area()!= 0). Чтобы тест выполнился успешно, метод area() должен вернуть любое ненулевое значение – это не очень полезный тест. Делайте тесты более конкретными. Если площадь прямоугольника должна быть равна 50, так и пишите: assertTrue(rectangle.area() == 50). Во многих реализациях xUnit присутствует специальное выражение assert() для тестирования равенства (эквивалентности). Отличительная его черта состоит в том, что вместо одного логического параметра выражение assertEquals() принимает два произвольных объекта и пытается определить, являются ли они эквивалентными. Преимущество состоит в том, что в случае неудачи выражение assertEquals() сгенерирует более информативное сообщение с указанием двух несовпадающих значений. Ожидаемое значение, как правило, указывается первым. Например, предыдущее выражение в среде JUnit можно переписать следующим образом: assertEquals(50, rectangle.area()).

Думать об объектах, как о черных ящиках, достаточно тяжело. Представим, что у нас есть объект Contract, состояние которого хранится в поле status и может быть экземпляром класса Offered или Running. В этом случае можно написать тест исходя из предполагаемой реализации:

Contract contract = new Contract(); // по умолчанию состояние Offered

contract.begin(); // состояние меняется на Running

assertEquals(Running.class, contract.status.class);

Этот тест слишком сильно зависит от текущей реализации объекта status. Однако тест должен завершаться успешно, даже если поле status станет логическим значением. Может быть, когда status меняется на Running, можно узнать дату начала работы над контрактом:

assertEquals(…, contract.startDate()); // генерирует исключение, если

// status является экземпляром Offered

Я признаю, что пытаюсь плыть против течения, когда настаиваю на том, что все тесты должны быть написаны только с использованием общедоступного (public) протокола. Существует специальный пакет JXUnit, который является расширением JUnit и позволяет тестировать значения переменных, даже тех, которые объявлены как закрытые.

Желание протестировать объект в рамках концепции белого ящика – это не проблема тестирования, это проблема проектирования. Каждый раз, когда у меня возникает желание протестировать значение переменной-члена, чтобы убедиться в работоспособности кода, я получаю возможность улучшить дизайн системы. Если я забываю о своих опасениях и просто проверяю значение переменной, я теряю такую возможность. Иначе говоря, если идея об улучшении дизайна не приходит мне в голову, ничего не поделаешь. Я проверяю значение переменной, смахиваю непрошеную слезу, вношу соответствующую отметку в список задач и продолжаю двигаться вперед, надеясь, что наступит день, когда смогу найти подходящее решение.

Самая первая версия xUnit для Smalltalk (под названием SUnit) обладала очень простыми выражениями assert. Если одно из выражений терпело неудачу, автоматически открывалось окно отладчика, вы исправляли код и продолжали работу. Среда разработки Java не настолько совершенна, к тому же построение приложений на Java часто выполняется в пакетном режиме, поэтому имеет смысл добавлять в выражение assert() дополнительную информацию о проверяемом условии. Чтобы в случае неудачи выражения assert() можно было вывести на экран дополнительную информацию.

В JUnit это реализуется при помощи необязательного первого параметра[19]. Например, если вы напишете assertTrue(«Должно быть True», false) и тест не сработает, то вы увидите на экране приблизительно следующее сообщение: Assertion failed: Должно быть True. Обычно подобного сообщения достаточно, чтобы направить вас напрямую к источнику ошибки в коде. В некоторых группах разработчиков действует жесткое правило, что все выражения assert() должны снабжаться подобными информационными сообщениями. Попробуйте оба варианта и самостоятельно определите, окупаются ли для вас затраты, связанные с информационными сообщениями.

Оглавление книги


Генерация: 1.522. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз