Как радикально улучшить покрытие кода?


21

Мне поручено получить устаревшее приложение под модульным тестом. Сначала немного информации о приложении: это 600-килобайтная кодовая база Java RCP с этими серьезными проблемами.

  • массовое дублирование кода
  • нет инкапсуляции, большая часть личных данных доступна извне, некоторые бизнес-данные также являются одиночными, поэтому их можно изменять не только извне, но и повсюду.
  • нет абстракций (например, нет бизнес-модели, бизнес-данные хранятся в Object [] и double [] []), поэтому нет OO.

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

Как мне начать атаковать проблему, чтобы быстро получить некоторое освещение , чтобы я мог показать прогресс менеджменту (и фактически начать зарабатывать на страховке из тестов JUnit)? Я не хочу использовать инструменты для создания наборов регрессионных тестов, например, AgitarOne, потому что эти тесты не проверяют, если что-то правильно.


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

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

1
@dodgy_coder Обычно соглашаются, но я надеюсь, что традиционный QA, который работает эффективно, спасет меня некоторое время.
Питер Кофлер

1
@dodgy_coder Майкл С. Перья, автор книги «Эффективная работа с устаревшим кодом», определяет старый код как «код без тестов». Это служит полезным определением.
StuperUser

Ответы:


10

Я полагаю, что есть две основные оси, по которым может быть размещен код, когда дело доходит до введения модульных тестов: A) насколько тестируемый код? и B) насколько он стабилен (т. е. насколько срочно требуются тесты)? Глядя только на крайности, это дает 4 категории:

  1. Код, который легко тестировать и ломкий
  2. Код, который легко тестировать и стабильный
  3. Код, который трудно тестировать и хрупкий
  4. Код, который сложно протестировать и стабильный

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


Отлично. У меня есть идея, как определить, легко ли это проверить с помощью статического анализа, например, счетчик зависимостей / Testability Explorer. Но как я могу определить, хрупок ли код? Я не могу сопоставить дефекты с определенными единицами (например, классами), и если это номер 3, конечно (классы бога / одиночные игры). Так, может быть, число проверок (горячих точек)?
Питер Кофлер

1
@Peter Kofler: фиксация горячих точек - хорошая идея, но наиболее ценным источником такого рода знаний будут разработчики, работающие с кодом.
Майкл Боргвардт

1
@Peter - как сказал Майкл, разработчики, которые работали с кодом. Любой, кто работал с большой кодовой базой в течение достаточного количества времени, будет знать, какие ее части пахнут. Или, если все это пахнет, какие его части действительно пахнут .
Carson63000

15

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

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

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

ИМХО, лучший подход - нацелиться на тестирование. Используйте метрики, интуицию и отчеты о дефектах, чтобы идентифицировать 1% или 10% кода, который создает наибольшее количество проблем. Хит эти модули сильно, и игнорировать остальные. Не пытайтесь делать слишком много, чем меньше, тем лучше.

Реальная цель состоит в том, что «поскольку мы внедрили UT, количество дефектов в тестируемых модулях сократилось до x% от тех, которые не входят в UT» (в идеале x - это число <100).


+1, вы не можете провести модульное тестирование чего-либо эффективно без более строгого стандарта, чем код.
dan_waterworth

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

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

2

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

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

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

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

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


1

Один из способов улучшить охват - написать больше тестов.

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

Представьте, что у вас есть 3 кодовых блока, a, b и b ', где b' является дубликатом (точной или почти пропущенной копии) B, и что у вас есть покрытие a и b, но не b 'с тестом T.

Если реорганизовать кодовую базу для устранения b 'путем извлечения общности из b и b' как B, кодовая база теперь выглядит как a, b0, B, b'0, где b0 содержит неразделенный код с b'0 и наоборот. и b0 и b'0 намного меньше, чем B, и вызывают / используют B.

Теперь функциональность программы не изменилась, и ни у одного из них нет теста T, поэтому мы можем снова запустить T и ожидать его прохождения. Теперь рассматривается код a, b0 и B, но не b'0. Кодовая база стала меньше (b'0 меньше, чем b '!), И мы все еще рассмотрим то, что мы изначально охватили. Наш коэффициент покрытия вырос.

Для этого вам нужно найти b, b 'и в идеале B, чтобы включить рефакторинг. Наш инструмент CloneDR может сделать это для многих языков, особенно для Java. Вы говорите, что в вашей кодовой базе много дублированного кода; Это может быть хорошим способом решения этой проблемы.

Как ни странно, акт поиска b и b 'часто увеличивает ваш словарный запас об абстрактных идеях, которые реализует программа. Хотя инструмент не имеет представления о том, что делают b и b, сам факт их выделения из кода, позволяющий просто сосредоточиться на содержимом b и b, часто дает программистам очень хорошее представление о том, какую абстракцию B_abstract реализует клонированный код. , Так что это также улучшает ваше понимание кода. Удостоверьтесь, что вы даете B хорошее имя, когда произносите его, и вы получите лучшее тестовое покрытие и более удобную программу.

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