Насколько строго я должен быть в "простейшем, что можно было бы работать" при выполнении TDD
Для 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: Я беру слишком буквальный материал "Делают простейшую вещь..."?
Ответы
Ответ 1
Я думаю, что ваш подход прекрасен, если вам это нравится. Вы не тратили время на то, чтобы писать глупый случай и решать его глупым способом - вы написали серьезный тест на настоящую желаемую функциональность и перешли его, как вы говорите, - самый простой способ, который мог бы работать. Теперь - и в будущем, когда вы добавляете все больше и больше реальных функций, вы гарантируете, что ваш код будет иметь желаемое поведение, чтобы выбрасывать правильное исключение в один конкретный плохо отформатированный файл. Что будет дальше, так это сделать это поведение реальным - и вы можете управлять этим, написав больше тестов. Когда становится проще писать правильный код, чем подделывать его снова, это когда вы напишете правильный код. Эта оценка варьируется среди программистов - и, конечно же, некоторые из них решат, что время, когда записывается первый неудачный тест.
Вы используете очень маленькие шаги, и это самый удобный подход для меня и некоторых других TDD. Если вам удобнее делать большие шаги, это тоже хорошо, но вы знаете, что всегда можете вернуться к более тонкому процессу в тех случаях, когда большие шаги вас трогают.
Ответ 2
Конечно, ваша интерпретация правила слишком буквальна.
Вероятно, это должно звучать так: "Сделайте простейшую потенциально полезную вещь..."
Кроме того, я думаю, что при написании реализации вы должны забыть те тела теста, которые вы пытаетесь удовлетворить. Вы должны помнить только имя теста (который должен рассказать вам о том, что он тестирует). Таким образом, вы будете вынуждены написать код, достаточно общий, чтобы быть полезным.
Ответ 3
Я тоже новичок в TDD, борющийся с этим вопросом. Во время исследования я нашел этот пост в блоге Роя Ошерова, который был первым и единственным конкретным и осязаемым определением "самой простой вещи, которая могла бы работать" что я нашел (и даже Рой признал, что это только начало).
В двух словах, Рой говорит:
Посмотрите на код, который вы только что написали в своем производственном коде, и спросите себя следующее:
"Могу ли я реализовать одно и то же решение таким образом, что это..."
- ".. Более жестко закодированный.."
- ".. Ближе к началу метода я написал его в.."
- ". Меньше с отступом (как можно меньше" областей ", таких как ifs, loop, try-catch).
- ".. короче (буквально меньше символов для записи), но все еще читаемый.."
"... и все еще пройти все тесты?"
Если ответ на один из них "да", то сделайте это и посмотрите, все тесты все еще проходят.
Ответ 4
Много комментариев:
-
Если проверка "empty.txt"
вызывает исключение, вы ее не поймаете.
-
Не повторяй себя. У вас должна быть одна тестовая функция, которая решает, выполняет ли проверка или не генерирует исключение. Затем дважды вызовите эту функцию с двумя различными ожидаемыми результатами.
-
Я не вижу никаких признаков модульного тестирования. Может быть, я скучаю по ним? Но просто использование assert
не будет масштабироваться для более крупных систем. Когда вы получаете результат от проверки, у вас должен быть способ объявить платформе тестирования, что данный тест с заданным именем преуспел или не удался.
-
Я встревожен тем, что проверка имени файла (в отличие от содержимого) представляет собой "проверку". На мой взгляд, это слишком просто.
Относительно вашего основного вопроса, я думаю, вы выиграете от более широкого представления о том, что самое простое. Я также не фундаменталистский TDDer, и мне было бы неплохо позволить вам немного "задуматься". Это означает, что мы думаем впереди сегодня или завтра утром, не думая впереди на следующей неделе.
Ответ 5
Вы пропустили пункт № 0 в своем списке: знаете, что делать. Вы говорите, что обрабатываете файл для целей проверки. После того, как вы указали, что означает "проверка" (подсказка: сделайте это, прежде чем писать какой-либо код), вы можете лучше понять, как: a) написать тесты, которые, ну, протестировать спецификацию, как реализовано, и b) написать простейшую вещь.
Если, например, валидация "должна быть XML", ваш тестовый пример - это просто некорректная строка, отличная от xml, и ваша реализация использует библиотеку XML и (при необходимости) преобразует ее исключения в те, которые указаны для вашего "валидация".
Ответ 6
Одно замечание для будущих учеников TDD - мантра TDD на самом деле не включает "Делайте простейшую вещь, которая могла бы работать". Kent Beck TDD Book имеет только 3 шага:
- Red - напишите небольшой тест, который не работает, и, возможно, даже не
сначала скомпилируйте.
- Зеленый - быстро выполнить тестовую работу, совершая
любые грехи, необходимые в этом процессе.
- Refactor - устранить все дублирование, созданное при простом тестировании.
Хотя фраза "Простейшая вещь..." часто приписывается Уорду Каннингему, он действительно задал вопрос "Какая самая простая вещь, которая могла бы быть работа?" , но этот вопрос позже превратился в команду, которую, по мнению Уорда, может смутить, скорее, помощь.
Изменить: я не могу рекомендовать читать Beck TDD Book достаточно сильно - ему нравится иметь сеанс сопряжения с самим мастером, давая вам идеи и мысли о процессе разработки, управляемом тестированием.
Ответ 7
Как метод должен делать только одну вещь, один тест должен проверять только одну вещь (поведение). Чтобы обратиться к приведенному примеру, я бы написал два теста, например, test_no_exception_for_empty_file
и test_exception_for_invalid_file
. На втором, действительно, может быть несколько тестов - по одному на вид недействительности.
Третий шаг процесса TDD должен интерпретироваться как "добавить новый вариант теста", а не "добавить новый вариант теста". Действительно, a unit test должен быть атомарным (протестируйте только одно) и обычно следует тройной шаблон A: Arrange - Act - Assert. И очень важно проверить, что тест сначала потерпел неудачу, чтобы убедиться, что он действительно что-то тестирует.
Я бы также отделил ответственность за чтение файла и проверку его содержимого. Таким образом, тест может передать буфер функции validate(), и тесты не должны читать файлы. Обычно модульные тесты не имеют доступа к файловой системе, поэтому это замедляет работу.