2010-02-23 4 views
12

Похоже, что во многих модульных тестах значения, которые параметризуют тест, либо запекаются самим тестом, либо объявляются заранее определенным образом.Недетерминизм в модульном тестировании

Например, вот тест взят из модульных тестов NUnit в (EqualsFixture.cs):

[Test] 
public void Int() 
{ 
    int val = 1; 
    int expected = val; 
    int actual = val; 

    Assert.IsTrue(expected == actual); 
    Assert.AreEqual(expected, actual); 
} 

Это имеет преимущество быть детерминированным; если вы один раз запустите тест, и он терпит неудачу, он будет продолжать сбой, пока код не будет исправлен. Тем не менее, вы заканчиваете тестирование только ограниченного набора значений.

Я не могу не чувствовать, что это пустая трата; точно такой же тест, вероятно, будет выполняться с теми же параметрами сотни, если не тысячи раз в течение всего жизненного цикла проекта.

Как насчет того, чтобы рандомизировать как можно больше входных данных во все модульные тесты, чтобы каждый запуск показывал что-то новое?

В предыдущем примере, может быть:

[Test] 
public void Int() 
{ 
    Random rnd = new Random(); 
    int val = rnd.Next(); 
    int expected = val; 
    int actual = val; 
    Console.WriteLine("val is {0}", val); 
    Assert.IsTrue(expected == actual); 
    Assert.AreEqual(expected, actual); 
} 

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

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

Полезно? Зло? Есть ли недостатки в этом? Неужели я полностью потерял точку модульного тестирования?

Спасибо за ваши мысли.

+8

Одним из главных недостатков является то, что если вы получаете отказ в тестировании, он не воспроизводится по своей природе. Очевидно, что вы можете регистрировать, какие значения использовались, когда произошел сбой, но это создает накладные расходы и возможности пропустить что-то. Однако то, что вы описываете, по-прежнему определенно полезно: см. Http://en.wikipedia.org/wiki/Fuzz_testing для получения информации о некоторых способах использования этой идеи и ее расширения. – itowlson

+0

Спасибо! Испытание Fuzz звучит точно, о чем я говорю. Я рассмотрю это больше. –

+0

«то же самое испытание, вероятно, выполняется с теми же параметрами, сколько сотни, если не тысячи раз в течение всего жизненного цикла проекта» - к счастью, мы изобрели компьютеры именно так, чтобы они могли выполнять повторяющиеся задачи в течение всего дня и не надоедать! – Ken

ответ

7

Вы хотите, чтобы ваши модульные тесты повторялись, чтобы они всегда вели себя одинаково, если код не изменится. Затем, если код изменится и приведет к сбою в модульном тесте, вы можете исправить код, и модульный тест выполнил свою задачу. Кроме того, вы знаете, что код [вероятно] исправлен, когда модульный тест проходит снова.

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

+0

Это имеет смысл, и я, безусловно, могу понять, насколько предсказуемость является активом. Однако некоторые из них относятся к рассматриваемым инструментам; если инструмент был разработан, чтобы точно отслеживать, как каждый параметр был параметризован, его результат можно было воспроизвести. Если бы программа тестирования поддерживала это, каковы были бы ваши мысли? –

+1

Это интересная идея, и, как указал Ивольсон, она уже существует. Я думаю, что предпочитаю, чтобы модульное тестирование было неслучайным, но для некоторых вещей может быть полезно использовать отдельный набор случайных тестов. Вероятно, вы также будете использовать случайные тесты по-разному, потому что имеет смысл запускать их несколько раз подряд. Создание инструментов для всех языков, для которых существуют инструменты xUnit, тоже будет проблемой. –

3

Тестирование должно охватывать ваши варианты использования, не более.

Посмотрите на PEX.

+0

Спасибо за ссылку. Pex выглядит как довольно невероятный инструмент. Его ответ на этот вопрос выглядит следующим образом: «Сделайте свои тесты детерминированными, но позвольте нам помочь вам найти интересные значения, которые вызывают разные пути исполнения, вместо того, чтобы вручную выбирать ваши значения для функций». Я думаю, что это долгий путь к ответу, но даже Pex не может заглянуть внутрь черного ящика ... Когда вы говорите варианты использования, как вы определяете прецедент? Если функция должна работать для бесконечного набора значений для X, как вы выбираете, какой? Спасибо за ваши мысли. –

+0

Если вы работаете с бесконечным (что касается ограничений размера данных), вам нужно определить конечность проблемы. Я бы рискнул предположить, что есть больше детерминизма, чем вы впервые видите. –

2

Большая проблема, с которой я сталкиваюсь, заключается в том, что, поскольку это случайное явление, это может не вызвать сбой, пока тест не будет запущен 200, 2000, 3000 или более раз. Если это терпит неудачу в течение 6007 месяцев или лет после того, как дело было написано, это по сути означает, что у вас была ошибка в течение месяцев или лет и она никогда не знала.

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

0

Испытываете ли вы случайный или оператор равенства?

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

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

4

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

Что действительно важно, так это то, что каждый тест должен всегда осуществлять один и тот же кодовый путь. Это не совсем то же самое.

Вы можете принять то, что я называю Constrained Non-Determinism в модульном тестировании. Это может привести вас к более Specification-Oriented способам написания тестов.

+0

Имея каждое тестовое упражнение, тот же путь кода имеет большой смысл. Мне понравился ваш пост по ограниченному не-детерминизму ... хорошая пища для размышлений, спасибо! –

0

Если ваше приложение не является детерминированным, вы тратите свое время на тестирование, если оно не является очень маленьким приложением.

Смежные вопросы