Несколько вопросов о модульных тестах

Два вопроса о модульных тестах.

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

    Кто-нибудь на самом деле следует этой методологии? На бумаге это кажется хорошей идеей, но так ли это на практике?

  2. Стоит ли вам писать модульные тесты, чтобы увидеть, как ваш метод обрабатывает неверный/вредоносный ввод? Очевидно, вы хотели бы написать тесты для функций, которые специально предназначены для обработки «пользовательского» ввода, чтобы увидеть, как он обрабатывает неверный/вредоносный ввод, но как насчет функций, которым никогда не следует передавать этот тип ввода? В какой точке вы проводите линию?

person tplaner    schedule 13.08.2010    source источник


Ответы (6)


Методология написания модульных тестов перед классами называется Разработка через тестирование (TDD) и была популяризирован Кентом Беком в начале 2000-х. Идея состоит в том, что вы пишете тест, описывающий необходимую вам функциональность. Первоначально этот тест не будет выполнен. Когда вы пишете свой класс, тест проходит. Вы реорганизуете свой тест, чтобы добавить желаемую функциональность, а затем реорганизуете класс, чтобы этот новый тест прошел. Ваш класс достиг своих целей, как только тесты пройдены. Конечно, это масштабируется и за пределами классов.

Какие типы тестов писать, зависит от того, тестируете ли вы общедоступный API или частный API. Для общедоступных API должны быть написаны более обширные тесты, чтобы убедиться, что входные данные правильно сформированы, особенно если вы не полностью доверяете пользователям своего API. Частные API (методы, которые вызываются только вашим кодом), вероятно, могут обойтись без этих тестов - я подозреваю, что вы можете доверять своей собственной команде разработчиков, чтобы не передавать им неверные данные.

person Thomas Owens    schedule 13.08.2010
comment
На самом деле, чтобы уточнить, вы не пишете ВСЕ свои тесты заранее. Вы пишете ОДИН тест. Это терпит неудачу. Вы пишете код, чтобы он прошел. Затем вы изменяете свой тест или пишете другой. Опять не получается. Напишите код. Это процесс, получивший название Red, Green, Refactor. - person CaffGeek; 13.08.2010
comment
Спасибо за это. Я добавлю это в свой ответ, просто для ясности. - person Thomas Owens; 13.08.2010

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

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

person dublev    schedule 13.08.2010

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

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

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

Также имеет значение, откуда поступают данные. Широкую общественность следует считать явно злонамеренной (т. е. сеть), сотрудников следует считать некомпетентными, и даже коллег-программистов (и вас самих!) следует считать как минимум небрежными. Но опасность отпадает по мере того, как вы приближаетесь к своему внутреннему кругу.

person Justin    schedule 13.08.2010
comment
Как сказал Чад, вам не нужно заранее писать все мыслимые тесты. Вместо этого вы можете начать с разумного набора тестов, которые определяют дизайн и спецификацию, а затем добавлять тесты, если вы обнаружите сбои или уязвимости, которые не обнаруживаются вашими текущими тестами. По сути, набор тестов находится в разработке, пока приложение находится в эксплуатации. - person Justin; 14.08.2010

Кто-нибудь на самом деле следует этой методологии?

Да.

На бумаге это кажется хорошей идеей, но так ли это на практике?

Да.

Стоит ли вам писать модульные тесты, чтобы увидеть, как ваш метод обрабатывает неверный/вредоносный ввод?

Да.

Как насчет функций, которым никогда не следует передавать этот тип ввода? В какой точке вы проводите линию?

Когда он переходит от программного обеспечения к психозу.

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

Вы пишете тесты для определенных вариантов использования. Вот и все.

Вы не придумываете случайные тестовые случаи, основанные на своем воображении.

Что, если? Что делать, если определенные варианты использования неполны? облом. Вы пишете тесты для официального, договорного, публичного интерфейса — и ничего более.

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

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

person S.Lott    schedule 13.08.2010
comment
И после того, как эти невозможные ситуации произойдут, вы добавите тест, чтобы воссоздать их, и они потерпят неудачу. Затем вы исправляете свой код. И в будущем вы уже испытали невозможное, и у вас есть испытание, чтобы невозможное больше не повторилось. - person CaffGeek; 13.08.2010
comment
Я не согласен с вашим только тем, что спецификация говорит о подходе. Это может быть уместно для контрактного программирования, но я думаю, что разработка продукта требует более активного подхода, поскольку часто нет формального разграничения между проектированием и разработкой. В большинстве моих работ, если дизайн плохой, мне некого винить, кроме себя и своих коллег. - person ChrisH; 13.08.2010
comment
@ChrisH: нет формального разграничения между дизайном и разработкой. ЛОЖЬ. Ваши тестовые примеры являются вашим дизайном. Вот почему TDD так эффективен. - person S.Lott; 13.08.2010
comment
Ваши тестовые случаи вы разрабатываете. Я согласен с этим. Однако в вашем ответе выше это не так. Ваш ответ предполагает, что вам не следует писать дополнительные тесты, кроме тех, которые основаны на вариантах использования, дизайне и спецификациях, которые я считаю внешними по отношению к самим модульным тестам. - person ChrisH; 13.08.2010
comment
@ChrisH: В большинстве организаций они внешние. В вашем случае они не внешние. Я подрядчик — работал почти в сотне разных мест — каждое уникально, поэтому невозможно сделать общее заявление. Тестовые случаи с формальным дизайном или без него часто навязываются извне. - person S.Lott; 13.08.2010

Существует разница в мышлении между тестом до и тестом после. Предварительное написание тестов — это форма дизайна, поскольку вы проектируете интерфейс своего кода и определяете ожидаемое поведение. Когда вы затем пишете код, который проходит тесты, это подтверждает ваш дизайн. И в конце разработки у вас уже есть набор тестов!

С test-after вам нужно быть осторожным, чтобы избежать ловушки написания тестов, которые ваш существующий код пройдет. Это другой фокус, и вы не получите от него столько же, сколько от версии Test-before.

person Grant Palin    schedule 13.08.2010
comment
Ловушка, которую вы упомянули во втором абзаце, именно поэтому писать тесты потом — плохая идея. Я не забочусь о полном покрытии в своих модульных тестах, но если я собираюсь написать тест, я всегда пишу его перед кодом. В противном случае слишком легко попасть в ловушку простого обратного проектирования ваших методов, чтобы гарантировать, что тесты всегда проходят. - person kubi; 13.08.2010
comment
@kubi Действительно. Это то, что я понял, когда начал заниматься TDD. - person Grant Palin; 14.08.2010

Уже есть довольно много ответов, но я хотел бы высказать еще одно мнение по вопросу номер 2.

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

person rasjani    schedule 13.08.2010