Как стать лучше при тестировании собственного кода


45

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

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

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


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

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

6
@ Брайан, я думаю, что ты должен совершать их независимо от того, используют ли их в настоящее время другие. Возможно, демонстрация хорошей практики заставит других следовать.
CaffGeek

Ответы:


20

Работа программиста - создавать вещи.

Работа тестера - ломать вещи.

Самое сложное - это сломать вещи, которые ты только что построил. Вы добьетесь успеха, только преодолев этот психологический барьер.


27
-1 Работа программиста - создавать вещи, которые работают . Это всегда связано с определенным количеством испытаний. Я согласен с тем, что есть необходимость в отдельной роли тестера, но это не единственная линия защиты.
Мэтью Родатус

8
@Matthew Rodatus - Объем тестирования, проводимого на стороне кодера, направлен только на проверку того, что должно работать на самом деле. На стороне тестера цель состоит в том, чтобы находить ошибки, а не наблюдать, как работает код.
mouviciel

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

2
@mouviciel - ложная дихотомия. Задача кодера - создавать вещи, которые работают, и он делает это, думая априори, при каких условиях его код должен работать. И это проверяется, по крайней мере, путем создания деструктивного теста + некоторого специального анализа границ (опять же, по крайней мере .) Кроме того, хороший кодер работает против спецификаций, а спецификации (когда они действительны) всегда тестируемы. Таким образом, хороший кодировщик разрабатывает тест, который проверяет выполнение этих требований в коде (и вы обычно делаете это путем написания тестов, которые изначально терпят неудачу, пока у вас не будет кода, который проходит тесты.)
luis.espinal

2
@ Дэйв Лэсли - это как раз моя точка зрения: архитектор не лучший человек, чтобы разрушить его дом: он слишком горд тем, насколько он силен, чтобы видеть его недостатки. Только другой архитектор (не парень с улицы) может объективно взглянуть на дом и обнаружить, что дом может сломаться при определенных условиях, которые бывший архитектор был слишком слеп, чтобы представить.
Mouviciel

14

Франциско, я собираюсь сделать здесь некоторые предположения, основываясь на том, что вы сказали:

«Мы не занимаемся ни TDD, ни модульным тестированием. Это мне очень поможет, но вряд ли это изменится».

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

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

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

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


11

Если вы пишете код на C, Objective-C или C ++, вы можете использовать CLang Static Analyzer, чтобы критиковать ваш источник, фактически не запуская его.

Доступны некоторые инструменты отладки памяти: ValGrind, Guard Malloc в Mac OS X, Electric Fence в * NIX.

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

Какой-то парень из Slashdot сказал, что он получил большую выгоду от пошагового создания новой строки исходного кода в отладчике. «Вот и все», сказал он. Я не всегда следую его совету, но когда он у меня был, он мне очень помог. Даже если у вас нет тестового примера, который стимулирует необычный путь кода, вы можете вертеть переменную в своем отладчике, чтобы взять такие пути, скажем, выделив некоторую память, а затем с помощью отладчика установить новый указатель на NULL вместо адрес памяти, затем пошаговое выполнение обработчика ошибок выделения.

Используйте утверждения - макрос assert () в C, C ++ и Objective-C. Если ваш язык не предоставляет функцию assert, напишите ее самостоятельно.

Используйте утверждения свободно, а затем оставьте их в своем коде. Я вызываю assert () "Тест, который продолжает тестирование". Я использую их чаще всего для проверки предварительных условий в точке входа большинства моих функций. Это одна из частей «Программирование по контракту», которая встроена в язык программирования Eiffel. Другая часть - это постусловия, то есть использование assert () в точках возврата функции, но я обнаружил, что я не получаю от этого столько же пробега, сколько предварительные условия.

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

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

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

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

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

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

Попробуйте убить ваше приложение с помощью отладчика или с помощью команды "kill -9" в * NIX, пока оно находится в середине написания большого, важного файла. Если ваше приложение хорошо спроектировано, весь файл будет записан или не будет записан вообще, или, может быть, если он будет записан только частично, то, что записывается, не будет повреждено, а сохраненные данные будут полностью использованы приложение после перечитывания файла.

базы данных всегда имеют отказоустойчивый дисковый ввод-вывод, но вряд ли какое-либо другое приложение имеет. Хотя журнализированные файловые системы предотвращают повреждение файловой системы в случае сбоя питания или сбоев, они вообще ничего не делают для предотвращения повреждения или потери данных конечного пользователя. За это отвечают пользовательские приложения, но вряд ли кто-либо, кроме баз данных, реализует отказоустойчивость.


1
+1 много практических советов, которые не нуждаются в поддержке кого-либо еще. Единственное, что я хотел бы добавить, это то, что assert предназначен для документирования и проверки условий, которые не могут быть выполнены, если в коде нет ошибки . Никогда не заявляйте о вещах, которые могут потерпеть неудачу из-за «неудачи», например, из-за отсутствия необходимого файла или неправильного ввода и т. Д.
Ян Голдби,

10

Когда я смотрю на тестирование своего кода, я обычно прохожу серию мыслительных процессов:

  1. Как мне разбить эту "штуку" на куски тестируемого размера? Как я могу выделить только то, что я хочу проверить? Какие заглушки / макеты я должен создать?
  2. Для каждого блока: Как проверить этот блок, чтобы убедиться, что он правильно реагирует на разумный набор правильных входных данных?
  3. Для каждого чанка: Как проверить, правильно ли чанк реагирует на неправильные входные данные (указатели NULL, недопустимые значения)?
  4. Как проверить границы (например, где значения переходят от знака к знаку без знака, от 8 до 16 бит и т. Д.)?
  5. Насколько хорошо мои тесты охватывают код? Есть ли какие-то условия, которые я пропустил? [Это отличное место для инструментов покрытия кода.] Если есть код, который был пропущен и не может быть выполнен, действительно ли он должен быть там? [Это совсем другой вопрос!]

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

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


5

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

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


1
+1 за цикломатическую сложность. «Мне действительно трудно следовать всем возможным путям, чтобы я мог находить ошибки», для меня это означает, что код OP может потребоваться разбить на более мелкие и менее сложные куски.
Тоби

@ Тоби Да, именно поэтому я решил добавить статический анализ. Если вы не можете проверить свой код, у вас есть проблемы. И если у вас есть одна проблема с вашим кодом, могут быть и другие. Используйте инструмент, чтобы найти потенциальные флаги предупреждения, оценить их и исправить по мере необходимости. У вас будет не только больше тестируемого кода, но и более читаемый код.
Томас Оуэнс

3

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

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


3

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

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

Учитывайте точку зрения пользователя . Попробуйте стать одним из пользователей, которые будут использовать ваше приложение или библиотеку функций.


1
+1 за упоминание о том, что видел вещи с точки зрения пользователя.
Мэтью Родатус

3

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

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

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

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


2
  • Напишите свои тесты, прежде чем писать свой код.
  • Каждый раз, когда вы исправляете ошибку, которая не была обнаружена тестом, напишите тест, чтобы поймать эту ошибку.

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


2

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

Лучший способ учиться - это видеть, как это делается, и извлекать уроки из этого.


2

Тестирование черного ящика! Вы должны создавать свои классы / методы с учетом тестирования. Ваши тесты должны быть основаны на спецификации программного обеспечения и должны быть четко определены на вашей диаграмме последовательности (через варианты использования).

Теперь, так как вы можете не захотеть заниматься разработкой на основе тестов ...

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

Но вы действительно должны проводить какое-то автоматическое тестирование; этот материал спасает жизни.


2

По моему опыту

Тестовый блок, если он не полностью автоматический, он бесполезен. Это больше похоже на то, что босс с заостренными волосами мог купить. Почему ?, потому что Test Unit обещал вам сэкономить время (и деньги), автоматизируя некоторые процессы тестирования. Но некоторые инструменты тестового модуля делают наоборот: это заставляет программистов работать странным образом и заставляет других создавать чрезмерные тесты. В большинстве случаев это не сэкономит рабочий час, но увеличит время перехода от QA к разработчику.

UML - это еще одна трата времени. одна доска + ручка может сделать то же самое, дешевле и быстрее.

Кстати, как быть хорошим в кодировании (и избежать ошибок)?

  • а) атомарность. Одна функция, которая выполняет одну простую (или несколько отдельных задач). Поскольку его легко понять, его легко отследить и легко решить.

  • б) гомология. Если, например, вы вызываете базу данных с помощью процедуры сохранения, выполните остальную часть кода.

  • в) Определить, уменьшить и изолировать «творческий код». Большая часть кода в значительной степени копируется и вставляется. Творческий код - это наоборот, код, который является новым и действует как прототип, он может потерпеть неудачу. Этот код подвержен ошибкам логики, поэтому важно его сократить, изолировать и идентифицировать.

  • d) код «Тонкий лед» - это код, который, как вы знаете, является «неправильным» (или потенциально опасным), но все еще нуждается, например, в небезопасном коде для многозадачного процесса. Избегайте, если можете.

  • e) Избегайте кода черного ящика, включая код, который вы не сделали (например, фреймворк) и регулярное выражение. С таким кодом легко пропустить ошибку. Например, я работал в проекте, используя Jboss, и обнаружил не одну, а 10 ошибок в Jboss (используя последнюю стабильную версию), это была PITA, чтобы найти их. Избегайте специально Hibernate, он скрывает реализацию, отсюда и ошибки.

  • е) добавить комментарии в свой код.

  • г) пользовательский ввод как источник ошибок. определить это. Например, SQL-инъекция вызвана пользовательским вводом.

  • h) Определить плохой элемент команды и отделить поставленную задачу. Некоторые программисты склонны испортить код.

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

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

  • кнопка работает? ... да.
  • база данных хранить что-нибудь? нет, значит ошибка была на среднем этапе.
  • тогда класс, который хранится в базе данных, работает? нет <- хорошо, я нашел ошибку. (это было связано с разрешением базы данных). Затем я проверил не только эту процедуру, но и каждую процедуру, которая делает то же самое (потому что гомология кода). Мне потребовалось 5 минут, чтобы отследить ошибку и 1 минуту, чтобы устранить ее (и многие другие ошибки).

И sidenote

Большую часть времени QA отстой (как отдельный раздел Dev), бесполезно, если они не работают в проекте. Они делают общий тест и больше ничего. Они не могут идентифицировать большинство логических ошибок. В моем случае я работал в престижном банке, программист закончил код и отправил его в QA. QA одобрило код и было запущено в производство ... затем код потерпел неудачу (эпическая ошибка), вы знаете, кто был обвинен ?. да, программист


2

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

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

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

  1. Отладка ; установить точки останова в коде и запустить приложение. Как только вы достигнете точки останова, проходите по линиям во время взаимодействия с приложением.
  2. Автоматизированное тестирование ; написать код, который проверяет ваш код. Это помогает только на уровнях ниже пользовательского интерфейса.
  3. Познакомьтесь с вашими тестерами ; они могут знать приложение лучше, чем вы, поэтому учитесь у них. Спросите их, в чем слабые стороны вашего приложения, и какую тактику они используют для поиска ошибок.
  4. Познакомьтесь с вашими пользователями ; научиться ходить в шкуре ваших пользователей. Функциональные требования являются отпечатком пальца ваших пользователей. Часто пользователи знают о приложении много вещей, которые могут быть не совсем понятны в функциональных требованиях. По мере того, как вы будете лучше понимать своих пользователей - характер их работы в реальном мире и то, как ваше приложение должно им помочь - вы лучше поймете, каким должно быть приложение.

2

Я думаю, что вы хотите работать по двум направлениям. Один из них - политический: заставить вашу организацию принять тестирование на каком-то уровне (с надеждой, что со временем они примут больше). Поговорите с инженерами QA за пределами вашего рабочего места. Найти списки книг QA . Покопайтесь в соответствующих статьях Википедии . Ознакомьтесь с принципами и методами обеспечения качества. Изучение этого материала подготовит вас к тому, чтобы сделать наиболее убедительные аргументы в своей организации. Хорошие отделы контроля качества существуют, и они вносят значительный вклад в свои организации.

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.