Модульное тестирование, равное и хеш-код - PullRequest
8 голосов
/ 23 апреля 2011

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

Например, следующее значение равно в объекте значения, который имеет 3 неизменных атрибута.Сложность кода составляет 14 (javaNCSS) и имеет 26 ветвей выполнения (Cobertura).Я должен также добавить, что я не могу выполнить сборку, если какой-либо метод имеет сложность больше 10.

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }

    TranscriptTaskDetails that = (TranscriptTaskDetails) o;

    if (inputFile != null ? !inputFile.equals(that.inputFile) : that.inputFile != null) {
        return false;
    }
    if (language != that.language) {
        return false;
    }
    if (outputFile != null ? !outputFile.equals(that.outputFile) : that.outputFile != null) {
        return false;
    }

    return true;
}

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

Я думаю об использовании EqualsBuilder и HashcodeBuilder из apache commons-langчтобы обойти это, но я не на 100% счастлив: S.

Edit

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

Ответы [ 8 ]

7 голосов
/ 23 апреля 2011

Я думаю об использовании EqualsBuilder и HashcodeBuilder из apache commons-lang, чтобы обойти это, но я не на 100% счастлив: S.

Почему бы не использовать их?

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

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

5 голосов
/ 23 апреля 2011

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

Простые факты:

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

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


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


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

3 голосов
/ 24 июля 2012

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

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }

    TranscriptTaskDetails that = (TranscriptTaskDetails) o;
    return Objects.equal(inputFile, that.inputFile) && Objects.equal(language, that.language) && Objects.equal(outputFile, that.outputFile);

}

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

Другой вариант, который я недавно начал использовать, это Lombok . Lombok - отличная библиотека для написания объектов с неизменяемыми значениями. Вот пример класса Ломбок:

@Data
public class Value {
    private final String field1;
    private final Foo field2;
}

Аннотация @Data заставляет lombok генерировать для вас кучу кода. Вы получаете методы получения (и для не финальных полей). Вы получаете equals, hashCode и toString. Вы получаете конструктор всех аргументов. И все эти методы автоматически обновляются при изменении списка полей. Я урезал сотни строк из моего текущего проекта с помощью Lombok, и мои объекты-ценности теперь всегда полностью реализованы.

3 голосов
/ 23 апреля 2011

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

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

3 голосов
/ 23 апреля 2011

Здесь есть противоречие.У вас нет никаких причин беспокоиться о сложности автоматически сгенерированного кода.Вы должны беспокоиться о кодировке руки.

1 голос
/ 14 мая 2013

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

0 голосов
/ 08 июля 2018

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

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

0 голосов
/ 23 апреля 2011

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

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

...