Как использовать TDD правильно для реализации численного метода?

Так как PDO использует объекты, они повышают Исключения, если ошибка происходит. Старые mysql/mysqli были простыми функциями и не выдавали исключения, они просто возвратили коды ошибки. Попытка/выгода используется, когда Исключение может быть выдано от кода, и Вы ловите его в пункте выгоды, который является объектно-ориентированным способом обработать ошибки. Вы не можете поймать Исключения с тем, если/еще блоки - они ничего не совместно используют с попыткой/выгодой.

10
задан Jader Dias 23 September 2009 в 02:15
поделиться

8 ответов

На этом этапе должен ли я:

  1. реализовать реальный код, который будет работать вне двух простых тестов?

  2. реализовать более тупой код, который будет работать только для двух простых тестов?

Ни то, ни другое. Я не уверен, откуда у вас подход «писать только один тест за раз», но это наверняка медленный путь.

Дело в том, чтобы писать четкие тесты и использовать это четкое тестирование для разработки вашей программы .

Итак, напишите достаточно тестов, чтобы проверить синусоидальную функцию. Два теста явно неадекватны.

В случае непрерывной функции вы должны в конечном итоге предоставить таблицу заведомо правильных значений. Зачем ждать?

Однако при тестировании непрерывных функций возникают некоторые проблемы. Вы не можете следовать глупой процедуре TDD.

Вы не можете протестировать все значения с плавающей запятой между 0 и 2 * pi. Вы можете' t проверить несколько случайных значений.

В случае непрерывных функций "строгий, бездумный TDD" не работает. Проблема здесь в том, что вы знаете, что ваша реализация синусоидальной функции будет основана на множестве симметрий. Вы должны тестировать на основе тех правил симметрии, которые вы используете. Жучки прячутся в трещинах и углах. Граничные и угловые случаи являются частью реализации, и если вы бездумно следуете TDD, вы не сможете это проверить.

Однако для непрерывных функций вы должны протестировать крайние и угловые случаи реализации.

Это не означает, что TDD не работает или неадекватен. Он говорит, что рабская преданность принципу «сначала испытание» не может работать без некоторого размышления о том, какова ваша настоящая цель.

Проблема здесь в том, что вы знаете, что ваша реализация синусоидальной функции будет основана на множестве симметрий. Вы должны тестировать на основе тех правил симметрии, которые вы используете. Жучки прячутся в трещинах и углах. Граничные и угловые случаи являются частью реализации, и если вы бездумно следуете TDD, вы не сможете это проверить.

Однако для непрерывных функций вы должны протестировать крайние и угловые случаи реализации.

Это не означает, что TDD не работает или неадекватен. Он говорит, что рабская преданность принципу «сначала испытание» не может работать без некоторого размышления о том, какова ваша настоящая цель.

Проблема здесь в том, что вы знаете, что ваша реализация синусоидальной функции будет основана на множестве симметрий. Вы должны тестировать на основе тех правил симметрии, которые вы используете. Жучки прячутся в трещинах и углах. Граничные и угловые случаи являются частью реализации, и если вы бездумно следуете TDD, вы не сможете это проверить.

Однако для непрерывных функций вы должны протестировать крайние и угловые случаи реализации.

Это не означает, что TDD не работает или неадекватен. Он говорит, что рабская преданность принципу «сначала испытание» не может работать без некоторого размышления о том, какова ваша настоящая цель.

Граничные и угловые случаи являются частью реализации, и если вы бездумно следуете TDD, вы не сможете это проверить.

Однако для непрерывных функций вы должны протестировать крайние и угловые случаи реализации.

Это не означает, что TDD не работает или неадекватен. Он говорит, что рабская преданность принципу «сначала испытание» не может работать без некоторого размышления о том, какова ваша настоящая цель.

Граничные и угловые случаи являются частью реализации, и если вы бездумно следуете TDD, вы не сможете это проверить.

Однако для непрерывных функций вы должны протестировать крайние и угловые случаи реализации.

Это не означает, что TDD не работает или неадекватен. Он говорит, что рабская преданность принципу «сначала испытание» не может работать без некоторого размышления о том, какова ваша настоящая цель.

9
ответ дан 3 December 2019 в 18:35
поделиться

Строгое следование TDD , вы можете сначала реализовать самый глупый код, который будет работать. Чтобы перейти к первому варианту (для реализации реального кода), добавьте больше тестов:

assertEqual(tan(x), sin(x)/cos(x))

Если вы реализуете больше, чем абсолютно требуется вашими тестами, тогда ваши тесты не будут полностью покрывать вашу реализацию. Например, если вы реализовали всю функцию sin () только с двумя приведенными выше тестами, вы можете случайно "сломать" его, вернув функцию треугольника (которая почти выглядит как синусоидальная функция), и ваши тесты не смогут обнаружить ошибку.

Еще одна вещь, о которой вам придется беспокоиться для числовых функций, - это понятие «равенства» и необходимость иметь дело с присущей потерей точности вычислений с плавающей запятой. Я подумал о вашем вопросе, прочитав только заголовок. :)

о чем я подумал , о чем будет ваш вопрос после прочтения только заголовка. :)

о чем я подумал , о чем будет ваш вопрос после прочтения только заголовка. :)

0
ответ дан 3 December 2019 в 18:35
поделиться

Я считаю, что переход к первому варианту происходит тогда, когда вы видите, что в вашем коде слишком много «если», «только для того, чтобы пройти тесты». Пока что этого не произошло, только с 0 и пи.

Вы почувствуете, что код начинает дурно пахнуть, и захотите провести его рефакторинг как можно скорее. Я не уверен, что это то, что говорит чистый TDD, но, IMHO, вы делаете это на этапе рефакторинга (неудачный тест, прохождение теста, цикл рефакторинга). Я имею в виду, если ваши неудачные тесты не требуют другой реализации.

1
ответ дан 3 December 2019 в 18:35
поделиться

В качестве строгого детского TDD вы можете реализовать немой метод, чтобы вернуться к зеленому цвету, а затем реорганизовать дублирование, присущее немому коду (проверка входного значения - это вид дублирования между тестом и кодом) путем создания реального алгоритма. Сложность в том, чтобы почувствовать TDD с таким алгоритмом, заключается в том, что ваши приемочные тесты действительно находятся прямо рядом с вами (таблица, предложенная С. Лоттом), поэтому вы как бы следите за ними все время. В более типичном TDD блок настолько отделен от целого, что приемочные тесты нельзя просто подключить прямо здесь, поэтому вы не начинаете думать о тестировании для всех сценариев, потому что все сценарии не очевидны.

Обычно у вас может быть реальный алгоритм после одного или двух случаев. В TDD важно то, что это движущий элемент дизайна, а не алгоритм. Когда у вас будет достаточно кейсов для удовлетворения потребностей дизайна, ценность TDD значительно упадет. Затем тесты больше превращаются в закрывающие угловые случаи, чтобы убедиться, что ваш алгоритм верен во всех аспектах, о которых вы можете подумать. Итак, если вы уверены в том, как построить алгоритм, дерзайте. Типы детских шагов, о которых вы говорите, подходят только тогда, когда вы не уверены. Делая такие маленькие шаги, вы начинаете выстраивать границы того, что должен покрывать ваш код, даже если ваша реализация на самом деле еще не реальна. Но, как я уже сказал, это больше для тех случаев, когда вы не уверены, как построить алгоритм.

Затем тесты больше превращаются в закрывающие угловые случаи, чтобы убедиться, что ваш алгоритм верен во всех аспектах, о которых вы можете подумать. Итак, если вы уверены в том, как построить алгоритм, дерзайте. Типы детских шагов, о которых вы говорите, подходят только тогда, когда вы не уверены. Делая такие маленькие шаги, вы начинаете выстраивать границы того, что должен покрывать ваш код, даже если ваша реализация на самом деле еще не реальна. Но, как я уже сказал, это больше для тех случаев, когда вы не уверены, как построить алгоритм.

Затем тесты больше превращаются в закрывающие угловые случаи, чтобы убедиться, что ваш алгоритм верен во всех аспектах, о которых вы можете подумать. Итак, если вы уверены в том, как построить алгоритм, дерзайте. Типы детских шагов, о которых вы говорите, подходят только тогда, когда вы не уверены. Делая такие маленькие шаги, вы начинаете выстраивать границы того, что должен покрывать ваш код, даже если ваша реализация на самом деле еще не реальна. Но, как я уже сказал, это больше для тех случаев, когда вы не уверены, как построить алгоритм.

Делая такие маленькие шаги, вы начинаете выстраивать границы того, что должен покрывать ваш код, даже если ваша реализация на самом деле еще не реальна. Но, как я уже сказал, это больше для тех случаев, когда вы не уверены, как построить алгоритм.

Делая такие маленькие шаги, вы начинаете выстраивать границы того, что должен покрывать ваш код, даже если ваша реализация на самом деле еще не реальна. Но, как я уже сказал, это больше для тех случаев, когда вы не знаете, как построить алгоритм.

5
ответ дан 3 December 2019 в 18:35
поделиться

Обратите внимание, что (в NUnit) вы также можете сделать

Assert.That(2.1 + 1.2, Is.EqualTo(3.3).Within(0.0005);

, когда имеете дело с плавающими -точечное равенство.

Я помню, как читал один совет, заключался в том, чтобы попытаться реорганизовать магические числа из ваших реализаций.

0
ответ дан 3 December 2019 в 18:35
поделиться

Вы должны кодировать все свои модульные тесты за один удар (на мой взгляд). Хотя идея создания только тестов, специально охватывающих то, что должно быть протестировано, верна, ваша конкретная спецификация требует функционирующей функции sine () , не a sine () , которая работает для 0 и PI.

Найдите источник, которому вы достаточно доверяете (друг-математик, таблицы в конце книги по математике или другая программа, в которой уже реализована функция синуса).

Я выбрал bash / bc , потому что мне лень вводить все вручную :-). Если бы это было функцией sine () , я бы просто запустил следующую программу и вставил ее в тестовый код. Я' d также поместите копию этого сценария в качестве комментария, чтобы я мог повторно использовать его, если что-то изменится (например, желаемое разрешение, если в этом случае больше 20 градусов, или значение PI, которое вы хотите использовать) .

#!/bin/bash
d=0
while [[ ${d} -le 400 ]] ; do
    r=$(echo "3.141592653589 * ${d} / 180" | bc -l)
    s=$(echo "s(${r})" | bc -l)
    echo "assertNear(${s},sine(${r})); // ${d} deg."
    d=$(expr ${d} + 20)
done

Это выводит:

assertNear(0,sine(0)); // 0 deg.
assertNear(.34202014332558591077,sine(.34906585039877777777)); // 20 deg.
assertNear(.64278760968640429167,sine(.69813170079755555555)); // 40 deg.
assertNear(.86602540378430644035,sine(1.04719755119633333333)); // 60 deg.
assertNear(.98480775301214683962,sine(1.39626340159511111111)); // 80 deg.
assertNear(.98480775301228458404,sine(1.74532925199388888888)); // 100 deg.
assertNear(.86602540378470305958,sine(2.09439510239266666666)); // 120 deg.
assertNear(.64278760968701194759,sine(2.44346095279144444444)); // 140 deg.
assertNear(.34202014332633131111,sine(2.79252680319022222222)); // 160 deg.
assertNear(.00000000000079323846,sine(3.14159265358900000000)); // 180 deg.
assertNear(-.34202014332484051044,sine(3.49065850398777777777)); // 200 deg.
assertNear(-.64278760968579663575,sine(3.83972435438655555555)); // 220 deg.
assertNear(-.86602540378390982112,sine(4.18879020478533333333)); // 240 deg.
assertNear(-.98480775301200909521,sine(4.53785605518411111111)); // 260 deg.
assertNear(-.98480775301242232845,sine(4.88692190558288888888)); // 280 deg.
assertNear(-.86602540378509967881,sine(5.23598775598166666666)); // 300 deg.
assertNear(-.64278760968761960351,sine(5.58505360638044444444)); // 320 deg.
assertNear(-.34202014332707671144,sine(5.93411945677922222222)); // 340 deg.
assertNear(-.00000000000158647692,sine(6.28318530717800000000)); // 360 deg.
assertNear(.34202014332409511011,sine(6.63225115757677777777)); // 380 deg.
assertNear(.64278760968518897983,sine(6.98131700797555555555)); // 400 deg.

Очевидно, вам нужно будет сопоставить этот ответ с тем, для чего предназначена ваша настоящая функция. Я считаю, что тест должен полностью подтверждать поведение кода в этой итерации. Если эта итерация должна была создать функцию sine () , которая работает только для 0 и PI, то это нормально. Но, на мой взгляд, это было бы серьезной тратой итераций.

Возможно, ваша функция настолько сложна, что ее придется выполнить за несколько итераций. Тогда ваш подход 2 верен, и тесты должны быть обновлены на следующей итерации , где вы добавляете дополнительные функции.

1
ответ дан 3 December 2019 в 18:35
поделиться

Я не знаю, какой язык вы используете, но когда я имею дело с числовым методом, я обычно сначала пишу простой тест, подобный вашему, чтобы убедиться в правильности схемы, а затем Я добавляю больше значений, чтобы охватить случаи, когда я подозреваю, что что-то может пойти не так. В .NET в NUnit 2.5 для этого есть удобная функция, которая называется [TestCase] ​​, где вы можете передать несколько входных значений в один и тот же тест, например:

[TestCase(1,2,Result=3)]   
[TestCase(1,1,Result=2)]     
public int CheckAddition(int a, int b)   
{  
 return a+b;   
}
0
ответ дан 3 December 2019 в 18:35
поделиться

Напишите тесты, проверяющие тождества.

Для примера sin(x) подумайте о формуле двойного угла и формуле половинного угла.

Откройте учебник по обработке сигналов. Найдите соответствующие главы и реализуйте каждую из этих теорем/короллариев в виде тестового кода, применимого к вашей функции. Для большинства функций обработки сигналов существуют тождества, которые должны соблюдаться для входов и выходов. Напишите тесты, которые проверяют эти тождества, независимо от того, какими могут быть входы.

Затем подумайте о входах.

  • Разделите процесс реализации на отдельные этапы. У каждого этапа должна быть цель. Тесты для каждого этапа должны проверять эту цель. (Примечание 1)
    1. Цель первого этапа - быть "приблизительно правильным". Для примера sin(x) это было бы похоже на наивную реализацию с использованием двоичного поиска и некоторых математических тождеств.
    2. Цель второго этапа - быть "достаточно точным". Вы попробуете разные способы вычисления одной и той же функции и посмотрите, какой из них дает лучший результат.
    3. Цель третьего этапа - быть "эффективным".

(Примечание 1) Сделать это работающим, сделать это правильным, сделать это быстрым, сделать это дешевым. - Приписывается Алану Кею

5
ответ дан 3 December 2019 в 18:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: