Добрый день.
В статье будет об одном совершенно незначительном моменте кодирования.
Например, многие мои коллеги говорили: «Дох, это нормально!» «Ой, что, всё хорошо, я нашла к чему придраться!» Но тем не менее.
Есть несколько вещей в написании кода, которые мне не нравятся.
Ну, на самом деле немало, чем старше я становлюсь, тем больше таких вещей происходит, мой характер не улучшается.
А вот наверху — когда разработчики пишут тесты для приватных методов.
О, нет! Когда для этой цели из метода удаляют слово «частный», и метод становится частным для пакета.
Здесь вы рассматриваете класс, около 3К строк, написанный в лучших традициях процедурного программирования, и там 10 публичных методов, 10 частных и еще 15 пакетно-приватных.
А про интерфейсы мы и не слышали — это сервис, давайте его так внедрим! Ок, думаете вы, теперь засучим рукава, вынесем все публичные методы в интерфейс, хелперы и их в отдельный пакет — короче, разрежем это безумие на части по SRP. Но затем вы обнаруживаете, что приватные методы пакета уже давно перетянуты из других мест. Итак, мы собрали все классы в один пакет! Если что-то можно использовать не по назначению, то, как говорится, оно обязательно будет использовано не по назначению.
Да-да, все знают, что для приватных методов писать тесты не нужно; если вы так сделаете, значит что-то не так с конструкцией и так далее.
Но блин, люди все равно это делают, и не зря, надо сказать.
Представьте, что упомянутый выше класс имеет нулевое тестовое покрытие.
Но нам нужно провести рефакторинг.
Так появляются методы, являющиеся частными для пакета.
Но что нам делать - выйти и Белая коробка Mockito, конечно, вырезали в PowerMock он остался , но ради этого перетащите PowerMock в проект, и синтаксис будет не лучше прямого вызова через рефлексию.
Можно, конечно, написать свой велосипед, но.
И все равно как лед не получается.
Но представьте, что для доступа к приватным элементам класса — методам, полям у нас есть интерфейс.
Какого черта, скажете вы, парень реально переутомился.
А вот так: Есть класс:
Тест:public class ObjectWithPrivates { private final AtomicInteger count = new AtomicInteger(0); private String name; private String methodToTest(String in) { return name + in + count.incrementAndGet(); } }
interface TestPrivates {
void setName(String name);
String methodToTest(String in);
AtomicInteger getCount();
}
@Test
void testPrivates() {
ObjectWithPrivates obj = new ObjectWithPrivates();
TestPrivates objWithPrivates = API.lookupPrivatesIn(obj)
.
usingInterface(TestPrivates.class);
objWithPrivates.setName("Andromeda");
String in = objWithPrivates.methodToTest("in");
AtomicInteger count = objWithPrivates.getCount();
Assertions.assertEquals("Andromedain1", in);
Assertions.assertEquals(1, count.get());
}
Как видите, с помощью интерфейса мы можем вызывать приватные методы и получать доступ к приватным полям.
Ну, по коду сразу видно, что происходит. Вы создаете интерфейс прямо в тесте, добавляете методы, соответствующие сигнатуре приватных методов и геттеры/сеттеры для приватных полей, и приводите свой объект к этому интерфейсу.
Имейте в виду, что все манипуляции происходят с obj и вы можете, например, посетить все приватные поля obj, а затем вызвать публичный метод. Вы можете получить доступ к закрытым полям/методам родительского класса: public class ObjectWithPrivates {
private static String name;
}
public class ObjectWithPrivatesSub extends ObjectWithPrivates {
private static String name;
}
interface TestPrivates {
void setName(String name);
}
@Test
void testPrivates() {
ObjectWithPrivatesSub obj = new ObjectWithPrivatesSub();
TestPrivates accessToPrivates = API.lookupPrivatesIn(obj)
.
lookupInSuperclass() .
usingInterface(TestPrivates.class);
accessToPrivates.setName("Vasya Pupkin");
:
}
Вы также можете использовать статические методы/поля: public class ObjectWithPrivates {
private static String name;
private static String whatIsYourName(){
return name;
}
}
interface TestPrivates {
void setName(String name);
private String whatIsYourName(){
}
@Test
void testPrivates() {
TestPrivates accessToPrivates = API.lookupPrivatesIn(ObjectWithPrivates.class)
.
usingInterface(TestPrivates.class);
accessToPrivates.setName("Vasya Pupkin");
Assertions.assertEquals("Vasya Pupkin", accessToPrivates.whatIsYourName());
:
}
Вы также можете создать объект, используя частный конструктор.
public class ClassC {
:
:
private ClassC(int a, String b, Object c, Long d) {
:
}
@Test
void createObject() {
ClassC classC = API.createInstanceOf(ClassC.class)
.
withArguments(new Integer(5), "yo!", new Long(123L), 15L);
:
}
При этом анализируется количество параметров и их типы и осуществляется поиск подходящего конструктора.
Под капотом стандартное отражение и Dynamic Proxy, никаких дополнительных зависимостей.
Вот ссылка на проект testprivates .
Используйте только для тестирования.
Всех с наступающим годом, не болейте! Теги: #java #чистый код #юнит-тестирование
-
Различные Типы Поставщиков Услуг Dsl
19 Oct, 24 -
Реактивные Веб-Технологии Переоценены
19 Oct, 24 -
40+ Стильных И Модных Наборов Иконок
19 Oct, 24 -
Вездесущий Bsod На Открытии Олимпиады
19 Oct, 24 -
Миграция Настольных Приложений На .Net Core
19 Oct, 24 -
Вы Форматируете Откровенный Контент?
19 Oct, 24