Я тестировал Строковый класс множителя с a multiply()
метод, который берет 2 числа в качестве исходных данных (как String
) и возвращает число результата (как String
)
public String multiply(String num1, String num2);
Я сделал реализацию и создал тестовый класс со следующими тестовыми сценариями, включающими параметр входной строки как
Теперь мои вопросы - они:
Я хотел бы знать, должен ли "каждый" assertEquals () быть в своем собственном методе тестирования? Или, действительно ли я могу сгруппироваться, подобные тестовые сценарии как testInvalidArguments () к содержит, все утверждает недопустимые символы вовлечения, так как Все они бросают тот же NumberFormatException?
При тестировании входного значения как символ ("a"), я должен включать тестовые сценарии для ВСЕХ сценариев? "a" как первый аргумент "a" как второй аргумент "a" и "b" как эти 2 аргумента
Согласно моему пониманию, преимущество этих модульных тестов состоит в том, чтобы узнать случаи, где вход от пользователя мог бы перестать работать и привести к исключению. И, затем мы можем дать пользователю со значимым сообщением (просьба, чтобы они обеспечили допустимый вход) вместо исключения. Это - корректное? И, действительно ли это - единственное преимущество?
Эти 11 тестовых сценариев упомянуты выше достаточные? Я пропускал что-то? Я переусердствовал? Когда достаточно?
При следовании из вышеупомянутой точки я успешно протестировал умножение () метод?
1) Существует компромисс между степенью детализации тестов (и, следовательно, простотой диагностики) и многословием кода вашего модульного теста. Я лично счастлив использовать относительно грубые методы тестирования, особенно после того, как тесты и проверенный код стабилизируются. Проблема детализации актуальна только тогда, когда тесты терпят неудачу. (Если я получаю сбой в тестовом сценарии с несколькими утверждениями, я либо исправляю первый сбой и повторяю, либо временно взламываю тестовый набор, если требуется, чтобы выяснить, что происходит.)
2) Используйте свой здравый смысл. Основываясь на вашем понимании того, как написан код, спроектируйте свои тесты, чтобы проверить все качественно разные подварианты. Помните, что невозможно проверить все возможные входные данные во всех случаях, кроме самых тривиальных.
3) Цель модульного тестирования - обеспечить уровень уверенности в том, что тестируемые методы выполняют то, что от них требуется. Что это означает, зависит от тестируемого кода. Например, если я выполняю модульное тестирование метода sort
, проверка пользовательского ввода не имеет значения.
4) Покрытие кажется разумным. Однако без подробного описания того, что должен делать ваш класс, и без изучения реальных модульных тестов, невозможно сказать, все ли вы охватили.Например, должен ли ваш метод работать с начальными / конечными пробельными символами, числами с десятичными точками, числами типа «123,456», числами, выраженными нелатинскими цифрами, числами с основанием 42?
5) Определите «успешно протестировано» . Если вы имеете в виду, доказывают ли мои тесты отсутствие ошибок в коде, то ответ однозначный «НЕТ».Если модульные тесты не перечисляют все возможные входные данные, они не могут служить доказательством правильности. (А в некоторых случаях даже тестирования всех входных данных бывает недостаточно.)
Во всех случаях, кроме самых тривиальных, тестирование не может доказать отсутствие ошибок. Единственное, что он может доказать, это наличие ошибок. Если вам нужно доказать, что в программе нет ошибок, вам нужно прибегнуть к «формальным методам»; т.е. применение формальных методов доказательства теорем к вашей программе.
И, как указывает другой ответ, вы должны предоставить его реальным пользователям, чтобы увидеть, что они могут придумать в виде неожиданного ввода. Другими словами ... являются ли заявленные или предполагаемые пользовательские требования полными и действительными.
Истинное количество тестов, конечно, бесконечно. Это непрактично. Вы должны выбрать допустимые репрезентативные кейсы. Похоже, вы это сделали. Молодец.
1) Я действительно считаю хорошей идеей ограничить количество утверждений, которые вы делаете в каждом тесте. JUnit сообщает только о первом сбое в тесте, поэтому, если у вас есть несколько утверждений, некоторые проблемы могут быть замаскированы. Более полезно иметь возможность видеть все, что прошло, и все, что не удалось. Если у вас есть 10 assertEquals
в одном тесте, а первый не прошел, тогда вы просто не знаете, что произошло бы с другими 9. Это были бы хорошие точки данных, которые нужно иметь при отладке.
2) Да, вы должны включить тесты для всех ваших входных данных.
3) Необходимо тестировать не только вводимые пользователем данные. Вы захотите написать тесты для любых общедоступных методов, которые могут дать сбой. В JUnit FAQ есть несколько хороших рекомендаций для этого, особенно в отношении геттеров и сеттеров.
4) Думаю, у вас это довольно хорошо описано. (По крайней мере, я не могу думать ни о чем другом, но вижу № 5).
5) Раздайте его некоторым пользователям для тестирования. Они всегда находят образцы данных, которые я никогда не думаю о тестировании. :)
Я просто хочу добавить, что с модульным тестированием вы можете получить еще больше, если сначала подумайте о возможных случаях, а затем внедрите в тестируемый development, потому что это поможет вам сосредоточиться на текущем случае, и это позволит вам создать самую простую возможную реализацию в режиме DRY. Вы также можете использовать какой-нибудь инструмент для покрытия тестов, например в Eclipse EclEmma, который действительно прост в использовании и покажет вам, выполнили ли тесты весь ваш код, что может помочь вам определить, когда этого достаточно (хотя это не доказательство, а просто показатель). Как правило, когда дело доходит до модульного тестирования, меня очень вдохновила книга Кента Бекса «Разработка через тестирование на примерах», я настоятельно рекомендую ее.
Модульное тестирование - это здорово (в проекте 200 KLOC, над которым я работаю, у меня столько же кода модульного теста, сколько и обычного кода), но (при условии правильного модульного теста):
Подумайте об этом так:
Это действительно важно понимать.
В дополнение к этому:
А затем, когда вы выполняете рефакторинг:
Но:
Это действительно фундаментально и должно быть модульное тестирование 101.
{{1 }}1) Лучше, чтобы ваши тесты были небольшими и целенаправленными. Таким образом, когда тест не проходит, становится ясно, почему тест не прошел. Обычно это приводит к одному утверждению для каждого теста, но не всегда.
Однако вместо ручного написания теста для каждого отдельного «недопустимого сценария» вы можете взглянуть на теории JUnit 4.4 (см. примечания к выпуску JUnit 4.4 и этот блог post ) или средство запуска тестов JUnit Parameterized .
Параметризованные тесты и теории идеально подходят для таких «расчетных» методов, как этот. Кроме того, чтобы все было организовано, я мог бы сделать два тестовых класса: один для «хороших» входных данных, а второй - для «плохих».
2) Вам нужно включить только те тестовые примеры, которые, по вашему мнению, наиболее вероятно обнаружат какие-либо ошибки в вашем коде, а не все возможные комбинации всех входных данных (что было бы невозможно, как WizardOfOdds указывает в своих комментариях). Три набора, которые вы предложили, хороши, но я, вероятно, не стал бы тестировать больше, чем эти три. Однако использование теорий или параметризованных тестов позволит вам добавить еще больше сценариев.
3) Написание модульных тестов дает множество преимуществ, не только то, о котором вы упомянули. Некоторые другие преимущества включают:
5) Похоже, вы хорошо поработали, придумав возможные сценарии тестирования. Думаю, у тебя есть все важные.