Считается ли «плохой практикой» проверка содержимого / кодировки файла в модульных тестах?


84

Немного контекста: ранее сегодня мне пришлось обновить некоторый код SQL, предоставленный другим моим коллегой, и, поскольку это довольно большой скрипт, он хранится в виде отдельного файла (который затем читается и выполняется во время выполнения). При этом я случайно представил две ошибки, которые были у нас несколько месяцев назад, а именно:

  • По какой-то причине файл ASCII был закодирован в UTF-16 (коллега прислал мне по электронной почте файл, который мог его вызвать).
  • В сценарии отсутствовали начальные SETоператоры (это требовалось из-за некоторых драйверов при работе, но не при локальной установке).

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

Однако, когда я нажал этот код, ко мне подошел другой коллега (который также является руководителем нашей команды) и сказал, что я не должен делать эти вещи снова, потому что:

«Эти вещи не входят в юнит-тесты»

«Модульные тесты должны использоваться только для проверки потока вашего кода»

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



35
« Модульные тесты должны использоваться только для проверки потока вашего кода », я бы сказал, что это фигня. Традиционно они должны включать все тесты, необходимые для обеспечения правильности «единицы», рассматриваемой отдельно. Если вы пишете только те модульные тесты, которые полезны для «проверки потока», что бы это ни значило, я надеюсь, что у вас также есть отдельные обширные наборы тестов (написанные отделом контроля качества?).
GBR

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

2
Кстати, эти тесты выглядят так, как будто вы хотите запускаться каждый раз, когда вы изменяете этот файл SQL. Здесь основной проблемой могут быть инструменты тестирования, которые могут не поддерживать режим работы «только если изменен»; из-за этого возникают реальные, конкретные проблемы, возможно, стоило бы включить функциональность «только в случае изменения» вручную с некоторым клуджем только для этих конкретных тестов.
GBR

5
Вместо того, чтобы проверить, что файл имеет правильное содержимое и кодировку, почему бы не проверить, что он работает ?
user253751

Ответы:


156

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

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


Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Томас Оуэнс

36

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

Какую проблему я пытаюсь решить?

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

Какие инструменты / процессы доступны для решения проблемы?

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

Если вы сделаете эту часть своей инфраструктуры сборки, например, включите ее в Jenkins или что-то в этом роде, ее можно будет применить ко всем файлам SQL в вашем проекте.

Модульные тесты решают проблему только для вашего текущего файла.

Как мне сообщить о необходимости инструмента?

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

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


21
При обсуждении соотношения цены и риска важно отметить, что это уже привело к потере времени из-за повторного введения ранее исправленных ошибок. Уже одно это является сильным аргументом для автоматизации проверки. +1
jpmc26

1
@ jpmc26, я полностью согласен. Обсуждение должно начаться с того факта, что вы потеряли много часов на то, чтобы понять, что случилось, и ваши юнит-тесты были вашей первой попыткой не дать остальной команде потерять такое же количество времени. Тем не менее, работа с руководителем группы, который должен отвечать перед руководством и заинтересованными сторонами, обычно очень ценится. Как руководитель группы, я хочу иметь возможность защищать инструменты / практики / код, которыми мы управляем. Если бы я увидел модульные тесты, которые нельзя отследить до реальных требований, я бы тоже был обеспокоен.
Берин Лорич

1
@BerinLoritsch Технически, это не модульный тест, я бы очень хотел знать, на какой «технике» вы основываете это утверждение. Насколько я могу судить, единого авторитетного определения модульных тестов не существует, и каждый пришел к собственному представлению о том, что такое « они ».
GBR

2
@gbr Существует неофициальное соглашение между разработчиками, что модульные тесты - это тесты, которые выполняются изолированно от внешних систем. Они проверяют только сам код, а не взаимодействие с файлами, базами данных или другими внешними сервисами. Википедия документирует это понимание: en.wikipedia.org/wiki/Unit_testing#External_work .
jpmc26

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

19

Неправильно называть тесты, обращающиеся к файлам, «Модульными тестами».

Он: «Эти вещи не относятся к юнит-тестам»

Вы: «Имеет смысл, но я не мог найти лучшего места, чтобы положить их. Где они принадлежат?»

К сожалению, какие виды тестов существуют и как они организованы, полностью зависит от компании. Поэтому вам нужно выяснить, как ваша компания справляется с этими тестами.

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


2
Неправильно называть тесты, обращающиеся к файлам, «Модульными тестами». Здесь файл, к которому осуществляется доступ, является исходным файлом. Он будет доступен так же, как и любой исходный файл (для его анализа). Тот факт, что тест, вероятно, не будет выполнен на том же языке, что и проверяемый «модуль» (SQL), делает его неортодоксальным, но не должен влиять на его классификацию в качестве модульного теста. продолжается ...
Gbr

1
... На самом деле правильная кодировка файла - это «тест», который выполняется любым компилятором каждый раз, когда он читает исходный файл. Здесь проблема заключается в том, что, будучи внешним файлом, интерпретируемым во время выполнения, «тесты компилятора» не будут запускаться автоматически, и поэтому вполне уместно добавить их явно, и я думаю, что это по праву можно считать «модульным тестом» этого Фрагмент SQL. И кажется разумным включать его в (потенциальный) набор тестов, запускаемых при каждой модификации файла.
GBR

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

2
@Warbo Это является плохой практикой для доступа к файлам (в целом), и (самое главное) причина заключается в том , что они не замедлятся , если они связаны с «ГЗ перечитал слоеную ссылку NFS», они медленно , если, например , со ссылкой на Майкл Перьев , они занимают 1/10 секунды. Это потому, что вы хотите запускать свои тесты как можно чаще, в идеале при каждом изменении, которое вы вносите в IDE, а когда у вас их много (как и должно быть), даже десятые секунды накапливаются в часы. (продолжение ...)
gbr

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

14

Майкл Фезерс говорит об этом в своей книге «Эффективная работа с устаревшим кодом»:

В отрасли люди часто спрашивают, являются ли конкретные тесты юнит-тестами. [...] Я возвращаюсь к двум качествам: тест проходит быстро? Может ли это помочь нам быстро локализовать ошибки?

Поможет ли ваш тест локализовать ошибки быстро и быстро? Если да, то сделай это! Если нет, то не надо! Это так просто!

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


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

1
@gbr Если результаты теста точны, важно только то, насколько быстро он выполняется. Если у меня есть 100 тестов, каждый из которых выполняется менее чем за 0,1 с, то в общей сложности они будут выполнены менее чем за 10 с. Я счастлив запускать их часто. Если у меня будет 1000 тестов, они займут до 1м40 с. Это слишком долго, я не буду часто их запускать, но я буду запускать их, как только внесу изменения с помощью небольшой группы из 100 тестов. Мне все равно, если это технически приемочный тест или что-то еще. Если если мне поможет найти ошибки раньше, я сделаю это независимо от семантики. Тест предоставляет значение только при запуске.
CJ Деннис

Я в основном согласен (независимость - еще одна очень важная вещь, например), но, тем не менее, было бы лучше, если бы Майкл Фезерс не усугубил путаницу в том, что означает «модульный тест». Не то, чтобы он был особенно виноват в этом беспорядке (и его книга превосходна, в частях, которые я до сих пор просматривал). Во всяком случае, я высказал довольно незначительное замечание.
GBR

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

Я перечитал этот раздел; Я не уверен, мне неясно, должно ли это быть (своего рода) определение или просто критерий. Но на самом деле « может ли это помочь нам быстро локализовать ошибки » вполне может подразумевать то, что он говорил раньше, об изоляции и т. Д. Так что я мог бы суетиться ни о чем (хотя один ваш ответ все еще может быть неверно истолкован)
gbr

10

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

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

Мой собственный термин для них - ресурсные тесты . ИМХО, они полностью оправданы, если выполняются ночью на CI-сервере: затраты минимальны и при разумном использовании явно повышают ценность. Одно из разумных определений : если тест проверяет проблему, вызвавшую проблему (например, кодировку, которую вы упоминаете).


4

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

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

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

Ваш SQL является кодом, как любой другой язык более низкого поколения, такой как C # или Java, и должен быть протестирован как таковой. А верификация и валидация относятся ко всем уровням тестирования. Таким образом, операторы кодирования и SET включены, но не обязательно проверяются исключительно. Обычные вещи, такие как окончания строк или огибающие, вы можете просто использовать ловушку SCM или функцию.

Лучшая практика состоит в том, чтобы иметь регрессионные тесты, чтобы гарантировать, что прошлые ошибки не будут повторно введены. Как правило, тесты создаются вместе с любым исправлением ошибки. Если эти ошибки не охватываются регрессионными тестами на уровне модуля / интеграции или системы, а затем вновь вводятся, то это проблема коллектива, проблема процесса, а не отдельная проблема.

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

Возвращаясь к отсутствующим инструкциям SET - они помогают проинформировать о множестве возможностей ввода и вывода для проверки. Какой тест вы бы написали, если бы вы потеряли какой-либо выбранный SET, если бы он пропустил?


Тестирование « модульного кода» является одним из подходов к модульному тестированию. По моему опыту, это приводит к хрупким тестам и огромному вздутию (например, чрезмерное издевательство) Альтернативный и, на мой взгляд, лучший подход к модульному тестированию - это «единица функциональности». Неважно, если какой-то функционал (скажем, «установка cookie при входе в систему») требует одного метода или дюжины взаимосвязанных процессов, это все равно одна единица.
Warbo

@Warbo - я бы назвал это ближе (но не) интеграционным тестированием. Модульное тестирование не требует чрезмерного или массивного чего-либо. Модульные тесты должны быть небольшими и быстрыми. На самом деле тестирование по функциональности приводит к тому, что вы описываете. Хрупкие тесты - это 1. те, которые больше или делают больше, чем должны. 2. сильно связаны 3. не несут никакой ответственности.
Росс

3

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

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

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


2

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

git add -p

При каждом изменении текстового файла рабочий каталог спросит вас, действительно ли вы хотите его сохранить. Это позволит вам увидеть, например, удаление этих «начальных SETутверждений».

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

git status

Здесь вы увидите файл выделен красным цветом, указывая , что есть изменения. Изучение причин, по которым они этого не сделали git add -p, быстро выявило бы проблему.


скажите, пожалуйста, как это поможет любому будущему разработчику избежать точно такой же проблемы? ... Суть автоматизированных тестов (также действительных для утверждений и разработки по контракту) заключается в том, что они, ну, ну, в общем, автоматизированы .
vaxquis

@vaxquis это предотвращает точно такую ​​же проблему - хотя и несколько случайно, как побочный эффект рабочего процесса, который является хорошей идеей по разным причинам. Я хочу сказать, что эта проблема может возникнуть во всех случаях, когда команда ОП не очень хорошо использовала свои VCS. - Ничего против автоматических тестов, но их значение - это тестирование семантических свойств, которые могут нарушиться из-за безобидных изменений в логике программы. Это не проверка всех возможных глупых путей, которыми исходный код может измениться.
оставил около

по вашей логике нам не нужны ремни безопасности; нам просто нужно вести себя более осторожно и вызывать меньше аварий ... Вы пропустили основной вопрос, поднятый OP - человеческая ошибка . Никакое количество VCS не может защитить вас от этого . Кроме того, FWIW: если тест может быть автоматизирован, он должен быть автоматизирован. Люди всегда самые слабые звенья в инженерных процессах. gitлучший пример этого - отличный инструмент, но едва пригодный для простых смертных .
vaxquis

@vaxquis Нет, нет! Ремни безопасности аналогичны тестам, которые имеют смысл: они улавливают множество ситуаций, которые могут произойти случайно. Тест на кодирование файла был бы аналогичен роботу, который следит за вами и не дает вам задыхаться, засунув бобы в нос.
Оставлено около

Согласно ФП, файлы в неправильном формате уже встречались дважды , поэтому , вероятно, они могут возникнуть случайно.
gnasher729

1

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


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

1
«Как сбой только во время выполнения делает его более сильным приложением?» Бросьте исключение, если проверка не удалась. В своем тесте просто убедитесь, что тестируемый раздел кода возвращает ожидаемый результат: это снимает бремя, чтобы проверить еще одну причину сбоя.
Доктор Джанлуиджи Зане Занеттини

1
Ваша стратегия (почти) не имеет ничего общего с модульным тестированием и автоматическим тестированием в целом, это другое дело, с различными применениями.
GBR

1
Я собирался предложить это. Ошибка - это деталь реализации; Я полагаю, что ответы были бы совсем другими, если бы хитрая кодировка была в приватном поле, а не в отдельном файле! Похоже, у OP есть 2 проблемы: файлы ресурсов могут быть плохо закодированы, а производство ведет себя иначе, чем dev. Проверяя файл во время выполнения, непосредственно перед его использованием, мы решаем вторую проблему: dev и prod выдают ту же ошибку. Модульные тесты могут затем сосредоточиться на реальной функциональности, а не на деталях реализации; эти «внутренние» проверки будут осуществляться так же, как частные методы.
Warbo

1
@ Dr.GianluigiZaneZanettini Бах ... Я сдаюсь ... Как я понимаю, в лучшем случае ваш ответ, после ваших "разъяснений" в комментариях, был не по теме (не ответ на вопрос), но на самом деле, как оно есть, это совершенно неправильно! не нужно писать дополнительные тесты ??? У меня недостаточно репутации, чтобы понизить это, но подумайте, как будто я это сделал. И я не думаю, что есть смысл продолжать этот разговор.
GBR

1

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

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

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


1
Автор нигде не говорит, что этот SQL-скрипт является частью какого-то теста, вы, похоже, неправильно поняли вопрос
gbr

Там трудно понять, я предполагаю, что сценарий SQL является частью теста.
h22

Ваш комментарий "Там трудно понять" ...
gbr

Трудно понять. Понижение вопроса.
h22

1

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

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

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

В прошлом я широко использовал подобные тесты - они спасли нас от боли.


И если кто-то захочет объяснить отрицательное мнение, я буду признателен
Murph

1

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

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

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

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

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