Не Нарушайте Мою Конфиденциальность

Добрый день.

В статье будет об одном совершенно незначительном моменте кодирования.

Например, многие мои коллеги говорили: «Дох, это нормально!» «Ой, что, всё хорошо, я нашла к чему придраться!» Но тем не менее.

Есть несколько вещей в написании кода, которые мне не нравятся.

Ну, на самом деле немало, чем старше я становлюсь, тем больше таких вещей происходит, мой характер не улучшается.

А вот наверху — когда разработчики пишут тесты для приватных методов.

О, нет! Когда для этой цели из метода удаляют слово «частный», и метод становится частным для пакета.

Здесь вы рассматриваете класс, около 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 #чистый код #юнит-тестирование

Вместе с данным постом часто просматривают: