Как привлечь внимание программиста в определенных условиях?

13

Давайте начнем с примера.

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

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

У меня есть две идеи, но у них обоих есть недостатки.

Умная оболочка «Читать все»

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

Что-то вроде этого:

def export():
    checker = AllReadChecker.new(table_row)

    name    = checker.get('name')
    surname = checker.get('surname')
              checker.ignore('age') # explicitly ignore the "age" field

    result = [name, surname] # or whatever

    checker.check_now() # check all is read

    return result

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

«Проверьте этот метод»

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

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


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

Вадим Пуштаев
источник
Может быть, вы можете генерировать exportна основе схемы?
coredump
Он не может быть сгенерирован автоматически, поэтому я должен попросить программиста взглянуть на код и принять решение.
Вадим Пуштаев,
Было бы решением добавить два списка имен полей (export и dont-export) в класс экспорта и провести модульный тест, который проверяет, что эти два списка вместе охватывают полный набор полей из базы данных?
Sjoerd Job Postmus
Можете ли вы автоматически сгенерировать тест, который проверяет, exportесть ли у вас все, что вам реально нужно?
Бизиклоп
1
комментарий в исходном коде слишком упрощенное решение? Обычно вещи пропускаются, потому что нет напоминания, комментарий исправит это.
gbjbaanb

Ответы:

11

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

Если exportотношение связано со схемой и схема изменилась, возможны два случая:

  • Либо exportвсе еще работает отлично, потому что это не было затронуто небольшим изменением схемы,

  • Или это ломается.

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

Почему ваша реализация неверна? Ну, по нескольким причинам.

  1. Это не имеет ничего общего с юнит-тестами ...

  2. ... и, на самом деле, это даже не тест.

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

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


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

Хотя я говорю конкретно о классах, это относится более или менее к другим объектам. Например, изменение схемы базы данных должно либо автоматически отражаться в вашем коде, например, с помощью генераторов кода, используемых многими ORM, либо, по крайней мере, должно быть легко локализовано: если я добавлю Descriptionстолбец в Productтаблицу и не буду использовать ORM или генераторы кода, Я, по крайней мере, ожидаю, чтобы сделать один изменение вData.Product классе DAL без необходимости поиска по всей базе кода и нахождения некоторых вхождений Productкласса, скажем, на уровне представления.

Если вы не можете разумно ограничить изменение одним местоположением (либо потому, что вы находитесь в случае, когда оно просто не работает, либо потому, что оно требует огромного количества разработки), тогда вы создаете риск регрессий . Когда я меняю классA и класс Bгде-то в базе кода перестает работать, это регрессия.

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

Во всех случаях избегайте полагаться в таких случаях только на комментарии. Что-то вроде:

// If you change the following line, make sure you also change the corresponding
// `measure` value in `Scaffolding.Builder`.

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

Арсений Мурзенко
источник
Да, этот «тест» на самом деле не тест, это своего рода ловушка, If you change the following line...которая работает автоматически и не может быть просто проигнорирована. Я никогда не видел, чтобы кто-то на самом деле использовал такие ловушки, отсюда и сомнения.
Вадим Пуштаев,
1
Проблема в том, что класс Bне перестает работать, возможно , он начинает работать неправильно. Но я не могу предсказать будущее и не могу написать тесты, которые предсказывают будущее, поэтому я пытаюсь напомнить разработчику, чтобы он писал этот новый тест.
Вадим Пуштаев
@VadimPushtaev: когда дело доходит до тестирования, «может быть , начинает работать неправильно» и «перестает работать» это точно то же самое. Вы не можете предсказать будущее, но вы должны быть в состоянии точно знать требования и проверить, что эти требования выполняются вашим реальным продуктом.
Арсений Мурзенко
Да, это вещь. Я на самом деле хочу напомнить разработчику подумать о новых требованиях . Я просто хочу махнуть рукой: «Здравствуйте, вы уверены, что не забыли об экспорте? Спросите менеджера или что-то, это общая проблема ».
Вадим Пуштаев
В любом случае, спасибо, ваш ответ помог мне организовать свои мысли, и у меня сейчас есть определенный план.
Вадим Пуштаев
3

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

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

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

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

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

Кейт Грегори
источник
2

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

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