Это код:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Это тест:
package com.XXX;
public FooTest {
@Test
void testValidatesThatBarWorks() {
int result = Foo.bar();
assertEquals(1, result);
}
@Test(expected = java.lang.IllegalAccessException.class)
void testValidatesThatClassFooIsNotInstantiable() {
Class cls = Class.forName("com.XXX.Foo");
cls.newInstance(); // exception here
}
}
Работает нормально, класс протестирован. Но Кобертура говорит, что закрытый конструктор класса не покрывается кодом. Как мы можем добавить тестовое покрытие к такому частному конструктору?
java
testing
code-coverage
yegor256
источник
источник
Ответы:
Что ж, есть способы потенциально использовать отражение и т. Д. - но действительно ли оно того стоит? Это конструктор, который нельзя вызывать , верно?
Если есть аннотация или что-то подобное, что вы можете добавить к классу, чтобы Cobertura понимала, что он не будет вызываться, сделайте это: я не думаю, что стоит пытаться добавить покрытие искусственно.
РЕДАКТИРОВАТЬ: Если нет возможности сделать это, просто живите с немного уменьшенным покрытием. Помните, что покрытие должно быть для вас полезным - вы должны отвечать за инструмент, а не наоборот.
источник
Я не совсем согласен с Джоном Скитом. Я думаю, что если вы можете легко выиграть, чтобы обеспечить покрытие и устранить шум в своем отчете о покрытии, то вам следует это сделать. Либо скажите своему инструменту покрытия игнорировать конструктор, либо отбросьте идеализм и напишите следующий тест и сделайте это с ним:
источник
constructor
? НеConstructor
следует параметризовать и не использовать исходный тип?constructor.isAccessible()
всегда возвращает false, даже в общедоступном конструкторе. Надо использоватьassertTrue(Modifier.isPrivate(constructor.getModifiers()));
.Хотя это не обязательно для покрытия, я создал этот метод, чтобы убедиться, что служебный класс хорошо определен, а также обеспечивает небольшое покрытие.
Я разместил полный код и примеры в https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test
источник
Modifier.isPrivate
качествеisAccessible
возвращалисьtrue
для частных строителей в некоторых случаях (насмешливых библиотеку помех?).Assert.utilityClassWellDefined()
в JUnit 4.12+. Вы рассматривали пул-реквест?setAccessible()
чтобы сделать конструктор доступным, вызывает проблемы для инструмента покрытия кода Sonar (когда я делаю это, класс исчезает из отчетов о покрытии кода Sonar).Я сделал закрытым конструктор моего класса статических служебных функций, чтобы удовлетворить CheckStyle. Но, как и на оригинальном плакате, у меня Кобертура жаловалась на тест. Сначала я попробовал этот подход, но это не влияет на отчет о покрытии, потому что конструктор никогда не выполняется. Итак, на самом деле все эти тесты заключаются в том, что конструктор остается закрытым - и это становится избыточным при проверке доступности в последующем тесте.
Я пошел с предложением Джавида Джамаэ и использовал отражение, но добавил утверждения, чтобы поймать кого-нибудь, кто возится с тестируемым классом (и назвал тест, чтобы указать высокие уровни зла).
Это слишком много, но я должен признать, что мне нравится теплое нечеткое ощущение 100% покрытия метода.
источник
fail(...)
не обязательно.С Java 8 можно найти другое решение.
Я предполагаю, что вы просто хотите создать служебный класс с несколькими общедоступными статическими методами. Если вы можете использовать Java 8, вы можете использовать
interface
вместо него.Конструктора и претензий от Cobertura нет. Теперь вам нужно протестировать только те строки, которые вам действительно нужны.
источник
Причина тестирования кода, который ничего не делает, состоит в том, чтобы достичь 100% покрытия кода и заметить, когда покрытие кода падает. В противном случае всегда можно было подумать, эй, у меня больше нет 100% покрытия кода, но это ВЕРОЯТНО из-за моих частных конструкторов. Это позволяет легко обнаруживать непроверенные методы, не проверяя, что это был частный конструктор. По мере роста вашей кодовой базы вы действительно почувствуете приятное теплое чувство, глядя на 100% вместо 99%.
ИМО, здесь лучше всего использовать отражение, так как в противном случае вам придется либо получить лучший инструмент покрытия кода, который игнорирует эти конструкторы, либо каким-то образом указать инструменту покрытия кода игнорировать метод (возможно, аннотацию или файл конфигурации), потому что тогда вы застрянете с помощью специального инструмента покрытия кода.
В идеальном мире все инструменты покрытия кода игнорировали бы частные конструкторы, принадлежащие к последнему классу, потому что конструктор используется как «мера безопасности», ничего больше :)
А затем просто добавляйте классы в массив по ходу.Я бы использовал этот код:
источник
Более новые версии Cobertura имеют встроенную поддержку игнорирования тривиальных геттеров / сеттеров / конструкторов:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
Игнорировать тривиальное
Игнорировать тривиальный позволяет исключить конструкторы / методы, содержащие одну строку кода. Некоторые примеры включают вызов только суперконструктора, методов получения / установки и т. Д. Чтобы включить тривиальный аргумент ignore, добавьте следующее:
или в сборке Gradle:
источник
Не надо. Какой смысл тестировать пустой конструктор? Поскольку в cobertura 2.0 есть возможность игнорировать такие тривиальные случаи (вместе с сеттерами / геттерами), вы можете включить его в maven, добавив раздел конфигурации в плагин cobertura maven:
В качестве альтернативы вы можете использовать Coverage аннотации :
@CoverageIgnore
.источник
Наконец-то есть решение!
источник
Enum<E>
действительно был перечислением ... Я считаю, что это лучше раскрывает намерение.Я не знаю насчет Cobertura, но я использую Clover, и в нем есть средства для добавления исключений сопоставления с образцом. Например, у меня есть шаблоны, исключающие строки протоколирования apache-commons, поэтому они не учитываются в покрытии.
источник
Другой вариант - создать статический инициализатор, похожий на следующий код
Таким образом, частный конструктор считается проверенным, а накладные расходы времени выполнения практически не поддаются измерению. Я делаю это, чтобы получить 100% покрытие с помощью EclEmma, но, вероятно, это работает для любого инструмента покрытия. Недостатком этого решения, конечно же, является то, что вы пишете производственный код (статический инициализатор) только для целей тестирования.
источник
ClassUnderTest testClass = Whitebox.invokeConstructor (ClassUnderTest.class);
источник
Иногда Cobertura отмечает код, не предназначенный для выполнения, как «не покрытый», в этом нет ничего плохого. Почему вас беспокоит
99%
страховое покрытие вместо100%
?Однако технически вы все еще можете вызывать этот конструктор с отражением, но для меня это звучит очень неправильно (в данном случае).
источник
Если бы я угадал цель вашего вопроса, я бы сказал:
Для 1 очевидно, что вы хотите, чтобы вся инициализация выполнялась с помощью заводских методов. В таких случаях ваши тесты должны уметь проверять побочные эффекты конструктора. Это должно подпадать под категорию обычного тестирования частного метода. Уменьшите размеры методов, чтобы они выполняли только ограниченное количество определенных действий (в идеале, только одно и одно хорошо), а затем протестируйте методы, которые на них полагаются.
Например, если мой [частный] конструктор устанавливает для полей экземпляра моего класса
a
значение5
. Тогда я могу (точнее, должен) протестировать это:Для 2 вы можете настроить clover для исключения конструкторов Util, если у вас есть установленный шаблон именования для классов Util. Например, в моем собственном проекте я использую что-то вроде этого (потому что мы следуем соглашению, согласно которому имена всех классов Util должны заканчиваться на Util):
Я намеренно пропустил
.*
следующее)
потому что такие конструкторы не предназначены для создания исключений (они не предназначены для чего-либо).Конечно, может быть и третий случай, когда вам может потребоваться пустой конструктор для некоммерческого класса. В таких случаях я бы порекомендовал вам поставить
methodContext
точную подпись конструктора.Если у вас много таких исключительных классов, вы можете изменить предложенный мной обобщенный частный конструктор reg-ex и удалить
Util
из него. В этом случае вам придется вручную убедиться, что побочные эффекты вашего конструктора по-прежнему протестированы и покрываются другими методами в вашем классе / проекте.источник
Test.java - это ваш исходный файл, в котором есть ваш частный конструктор
источник
Следующее работало со мной над классом, созданным с помощью аннотации Lombok @UtilityClass, которая автоматически добавляет частный конструктор.
Хотя constructor.setAccessible (true) должен работать, когда частный конструктор был написан вручную, с аннотацией Lombok не работает, поскольку она заставляет его. Constructor.newInstance () фактически проверяет, что конструктор вызывается, и этим завершается покрытие самого конструктора. С помощью assertThrows вы предотвращаете сбой теста и управляете исключением, поскольку это именно та ошибка, которую вы ожидаете. Хотя это обходной путь, и я не ценю концепцию «покрытия строки» и «покрытия функциональности / поведения», мы можем найти смысл в этом тесте. Фактически, вы уверены, что у служебного класса есть частный конструктор, который правильно генерирует исключение при вызове также через рефлексию. Надеюсь это поможет.
источник
Мой предпочтительный вариант в 2019 году: использовать ломбок.
В частности,
@UtilityClass
аннотация . (К сожалению, только «экспериментальный» на момент написания, но он работает нормально и имеет положительные перспективы, поэтому, вероятно, скоро будет обновлен до стабильной версии.)Эта аннотация добавит частный конструктор для предотвращения создания экземпляра и сделает класс окончательным. В сочетании с
lombok.addLombokGeneratedAnnotation = true
inlombok.config
практически все среды тестирования будут игнорировать автоматически сгенерированный код при вычислении покрытия тестами, что позволяет обойти покрытие этого автоматически сгенерированного кода без взлома или отражения.источник
Вы не можете.
Очевидно, вы создаете частный конструктор, чтобы предотвратить создание экземпляра класса, который должен содержать только статические методы. Вместо того, чтобы пытаться охватить этот конструктор (что потребовало бы создания экземпляра класса), вы должны избавиться от него и доверять своим разработчикам, чтобы они не добавляли методы экземпляра в класс.
источник