Типичное заблуждение при тестировании — воображать, что тестировать необходимо именно то, что делает реализация. На первый взгляд в этом не видно ничего дурного. Однако если сформулировать эту проблему иначе, она становится понятнее: частой ошибкой при тестировании является привязка тестов к особенностям реализации, тогда как эти особенности являются случайными и не имеют отношения к требуемой функциональности.
Когда тесты жестко связаны с особенностями реализации, изменения в реализации, которые в действительности совместимы с требуемым поведением, могут привести к отказу тестов. Из-за этого будут возникать ложные сообщения об ошибках. Обычно программисты реагируют на это исправлением теста или исправлением кода. Принятие ложного срабатывания за истинное часто есть следствие страха, неуверенности и сомнений. Это равносильно принятию случайного поведения за требуемое. При исправлении теста программист либо изменяет тест так, чтобы он проверял требуемое поведение (что хорошо), либо привязывает тест к новой реализации (что плохо). Тесты должны быть достаточно точными, но они должны быть и правильными.
Например, требования к тройственному сравнению, такому как String.compareTo в Java или strcmp в C, таковы: результат должен быть отрицательным, если левая часть меньше правой, положительным, если левая часть больше правой, и нулем, если они равны. Такого рода сравнение используется во многих API, включая функцию сравнения для qsort в C и compareTo в интерфейсе Java Comparable. Обычно в реализациях для обозначения меньше и больше используются конкретные значения -1 и +1, в связи с чем программисты иногда ошибочно предполагают, что именно эти значения и выражают действительное требование, и пишут тесты, основанные на этом предположении.
Похожая проблема возникает, когда тесты жестко привязаны к количеству пробелов, наличию определенных слов и другим аспектам форматирования и представления текста, которые являются особенностью реализации. Наличие пробелов не должно влиять на результат, если только вы не пишете, скажем, XML-генератор с настройкой форматирования. Аналогичным образом жестко фиксированное расположение кнопок и меток на элементах управления пользовательского интерфейса уменьшает возможность в будущем менять эти несущественные детали. Мелкие изменения в реализации и несущественные изменения в форматировании внезапно приводят к тому, что проект не собирается.
Слишком конкретные тесты часто создают проблему при модульном тестировании по принципу белого ящика. Тесты белого ящика используют структуру кода, чтобы определить, какие тесты нужны. Типичная ошибка при таком тестировании — создание тестов, которые в конечном итоге проверяют, что код делает то, что он делает. Простое подтверждение того, что и так очевидно из кода, не имеет никакой ценности и приводит к возникновению ложного чувства прогресса и надежности.
Чтобы быть эффективными, тесты должны проверять обязательства по контракту, а не бессмысленно повторять реализацию. Они должны рассматривать тестируемые модули как черные ящики, описывая контракты интерфейса в исполняемой форме. Поэтому следите, чтобы проверяемое поведение совпадало с требуемым поведением.