Для TDD Вы имеете к
С этим подходом Вы предполагаете для покрытия всех случаев (который прибывает по моему мнению, по крайней мере), но я - удивление, если я являющийся слишком строгим здесь и если возможно "предположить" некоторые сценарии вместо простого, обнаруживают их.
Например, я обрабатываю файл и если он не соответствует определенному формату, я должен бросить InvalidFormatException
Таким образом, мой первый тест был:
@Test
void testFormat(){
// empty doesn't do anything nor throw anything
processor.validate("empty.txt");
try {
processor.validate("invalid.txt");
assert false: "Should have thrown InvalidFormatException";
} catch( InvalidFormatException ife ) {
assert "Invalid format".equals( ife.getMessage() );
}
}
Я выполняю его, и это перестало работать, потому что это не выдает исключение.
Таким образом, следующая вещь, которая прибывает по моему мнению: "Сделайте самую простую вещь, которая могла возможная работа", таким образом, я:
public void validate( String fileName ) throws InvalidFormatException {
if(fileName.equals("invalid.txt") {
throw new InvalidFormatException("Invalid format");
}
}
Doh!! (хотя реальный код немного более сложен, я нашел мой сам выполнение чего-то вроде этого несколько раз),
Я знаю, что должен в конечном счете добавить другое имя файла и другой тест, который сделал бы этот подход непрактичным, и это вынудит меня осуществить рефакторинг к чему-то, что имеет смысл (который, если я понял правильно, точка TDD, для обнаружения шаблонов, которые использование представляет), но:
Q: я беру слишком литеральный, "Делают самую простую вещь..." материал?
Я думаю, что ваш подход хорош, если он вас устраивает. Вы не стали тратить время на написание глупого примера и решение его глупым способом - вы написали серьезный тест для реальной желаемой функциональности и заставили его пройти - как вы говорите - самым простым способом, который только может работать. Теперь - и в будущем, когда вы добавляете все больше и больше реальной функциональности - вы гарантируете, что ваш код имеет желаемое поведение - выбрасывать правильное исключение на один конкретный плохо отформатированный файл. Далее необходимо сделать это поведение реальным - и вы можете добиться этого, написав больше тестов. Когда становится проще написать правильный код, чем снова его сымитировать, тогда вы и напишете правильный код. Эта оценка варьируется среди программистов - и, конечно, некоторые решат, что это время наступает, когда написан первый неудачный тест.
Вы используете очень маленькие шаги, и это самый удобный подход для меня и некоторых других TDDеров. Если вам удобнее использовать большие шаги, это тоже хорошо - но знайте, что вы всегда можете вернуться к более тонкому процессу в тех случаях, когда большие шаги вас подведут.
Конечно, ваша интерпретация правила слишком буквальна. Возможно, оно должно звучать так: "Сделай самую простую потенциально полезную вещь..."
Также, я думаю, что при написании реализации вы должны забыть тело теста, который вы пытаетесь удовлетворить. Вы должны помнить только название теста (которое должно рассказать вам о том, что он проверяет). Таким образом, вы будете вынуждены писать код достаточно общим, чтобы быть полезным.
Много комментариев:
Если при проверке "empty.txt"
возникает исключение, вы его не ловите.
Не повторяйтесь. У вас должна быть одна тестовая функция, которая решает, выбрасывает ли валидация исключение или нет. Затем вызовите эту функцию дважды, с двумя разными ожидаемыми результатами.
Я не вижу никаких признаков фреймворка для юнит-тестирования. Может быть, я их упускаю? Но простое использование assert
не позволит масштабировать большие системы. Когда вы получаете результат от проверки, у вас должен быть способ сообщить системе тестирования, что данный тест с данным именем прошел успешно или не прошел.
Меня настораживает идея, что проверка имени файла (в отличие от содержимого) представляет собой "валидацию". На мой взгляд, это немного слишком просто.
Что касается вашего основного вопроса, то я думаю, что вы выиграете от более широкого представления о том, что такое простая вещь. Я также не являюсь фундаменталистом TDDer, и я не против того, чтобы позволить вам "думать наперед" на немного. Это значит, что нужно думать о сегодняшнем дне или завтрашнем утре, а не о следующей неделе.
Как метод должен делать только одну вещь, один тест должен проверять только одну вещь (поведение). Для примера я бы написал два теста, например, test_no_exception_for_empty_file
и test_exception_for_invalid_file
. Во втором случае может быть несколько тестов - по одному на каждый вид недействительности.
Третий шаг процесса TDD должен интерпретироваться как "добавить новый вариант теста", а не "добавить новый вариант к тесту". Действительно, модульный тест должен быть атомарным (тестировать только одну вещь) и обычно следует шаблону тройки А: Arrange - Act - Assert. И очень важно сначала проверить, что тест не сработал, чтобы убедиться, что он действительно что-то тестирует.
Я бы также разделил ответственность за чтение файла и проверку его содержимого. Таким образом, тест может передать буфер функции validate(), и тестам не придется читать файлы. Обычно модульные тесты не обращаются к файловой системе, потому что это замедляет их работу.
Я тоже новичок в TDD, который борется с этим вопросом. В процессе поиска я нашел эту запись в блоге Роя Ошерова, которая была первым и единственным конкретным и осязаемым определением "самой простой вещи, которая может работать", которое я нашел (и даже Рой признал, что это только начало).
В двух словах, Рой говорит:
Посмотрите на код, который вы только что написали, в вашем производственном коде и спросите себя:
"Могу ли я реализовать то же самое решение способом, который ..."
"... и все еще заставляет все тесты проходить?"
Если ответ на один из этих вопросов "да", то сделайте это, и увидите, что все тесты все еще проходят.
Вы пропустили пункт #0 в вашем списке: знать, что делать. Вы говорите, что обрабатываете файл в целях проверки. Как только вы определите, что означает "валидация" (подсказка: сделайте это до написания кода), вы сможете лучше понять, как а) писать тесты, которые, ну, проверяют спецификацию как реализованную, и б) писать простейшие вещи.
Если, например, валидация - это "должно быть XML", ваш тестовый пример - это просто некоторая строка, не соответствующая XML, а ваша реализация - это использование библиотеки XML и (при необходимости) преобразование ее исключений в те, которые указаны для вашей функции "валидации".