Как далеко вы заходите в охвате кода?[закрыто]

StackOverflow https://stackoverflow.com/questions/253587

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

//this class is meant as a pseudo-enum - I'm stuck on Java 1.4 for time being
public final class BillingUnit {

    public final static BillingUnit MONTH = new BillingUnit("month");
    public final static BillingUnit YEAR = new BillingUnit("year");

    private String value;

    private BillingUnit(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    public boolean equals(Object obj) {
        return value.equals(((BillingUnit) obj).getValue());

    }

    public int hashCode() {
        return value.hashCode();
    }
}

Я написал несколько простых модульных тестов, чтобы убедиться, что equals() работает корректно, что getValue() возвращает то, что я ожидал, и т.д.Но благодаря визуальному характеру EclEmma, hashcode() метод отображается ярко-красным цветом для обозначения "не проверено".

Стоит ли вообще утруждать себя тестированием hashCode(), в этом примере, учитывая, насколько проста реализация?Мне кажется, я бы добавил модульный тест для этого метода просто для того, чтобы увеличить процент покрытия кода и избавиться от яркого красного выделения, которое EclEmma добавляет к этим строкам.

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

Это было полезно?

Решение

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

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

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

Другие советы

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

if(logger.isDebugEnabled()) {
    logger.debug("something");
}

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

Просто для ясности, причина, по которой я считаю, что вышеуказанные ситуации следует исключить из освещения, заключается в том, что:

  • Не тестировать эту функциональность - это нормально.Вам не нужно запускать весь ваш набор тестов по 5 раз с библиотекой ведения журнала, установленной для каждого уровня ведения журнала, просто чтобы убедиться, что все ваши инструкции выполнены.
  • Если бы вы сделали вышеописанное, это исказило бы ваш охват в другую сторону.Если 90% ваших филиалов находятся if(log.isDebugEnabled()), и вы тестируете их все, но никаких других ветвей, будет выглядеть, что у вас 90% покрытие ветвей (хорошо), когда на самом деле у вас 0% нетривиального покрытия ветвей (плохо!!).

Существует разница между покрытием кода и покрытием тестирования.Вы должны попытаться убедиться, что ваш код адекватно протестирован, а не имеет 100% покрытия кода.

Рассмотрим следующий код:

public float reciprocal (float ex)
{
    return (1.0 / ex) ;
}

Если бы вы запустили один тест, который прошел со значением 1.0, то вы получили бы 100% покрытие кода (ветвление и оператор) со всеми проходами.Очевидно, что в коде есть дефект.

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

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

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

Что касается тестирования equals, hashCode и некоторые другие основанный на контракте интерфейсы, такие как Comparable и Serializable, Я действительно включаю эти тесты.Важно, чтобы equals/hashCode контракт реализован правильно, аналогично с equals/Comparable.

Видишь JUnit-Дополнения, особенно

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

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

Кроме того, для TDD вам хотелось бы 100% покрытия, потому что тогда вы можете быть уверены (или очень близки к этому), что вы ничего не нарушаете при рефакторинге.

Мой коллега-программист, как и я, застрял на старом Java1.4 ;)

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

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

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

Если бы вас не беспокоило покрытие кода, то я бы предположил, что вам понадобились бы тесты для equals() и getValue() - в зависимости от того, как и где hashCode() используется (извините, я разработчик C #), тогда вы мог бы хочу это проверить.

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

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

Это даст вам покрытие кода, и в придачу это даст вам некоторую уверенность в том, что hashCode действительно удовлетворяет своему контракту :-)

Это имеет лишь незначительный смысл, учитывая, что вы, очевидно, можете доверять методу hashCode String, и вы не ожидаете, что когда-либо измените этот класс.Но если вы достаточно подозрительны к своему методу equals, чтобы вообще его тестировать, то вы должны быть достаточно подозрительны к нему, чтобы проверить, что он и хэш-код остаются согласованными.И вы всегда должны с подозрением относиться к предположениям, что в будущем вам не захочется возиться с тем, что вы делали раньше.Например, если кто-то приходит и решает "оптимизировать", добавив проверку на равенство указателей, то у вас также может быть полный набор тестов для них, чтобы запустить их измененный код.В противном случае они будут тратить время на то же беспокойство, что и вы - это нормально, что у меня нет покрытия кода?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top