Тестируемый модуль: Impl или Interface?
=======================================

Предположим, у меня есть интерфейс и класс реализации, который его реализует, и я хочу написать для этого модульный тест. Что я должен проверить интерфейс или Impl?

Вот пример:

Итак, у меня есть HelloInterface и HelloInterfaceImpl, который его реализует. Что такое тестируемый интерфейс или Impl?

Я думаю, что это должен быть HelloInterface. Рассмотрим следующий эскиз теста JUnit:

Основная линия на самом деле та, которую я закомментировал.

`((HelloInterfaceImpl)hi).setTarget(target);`

метод`setTarget()` не является частью моего открытого интерфейса, поэтому я не хочуaccidentally назови это. Если я действительно хочу это назвать, я должен уделить минутку и подумать об этом. Это помогает мне, например, обнаружить, что я действительно пытаюсь сделать это внедрение зависимости. Это открывает для меня целый мир новых возможностей. Я могу использовать какой-то существующий механизм внедрения зависимостей (например, Spring), я могу имитировать его сам, как я это делал в своем коде, или использовать совершенно другой подход. Присмотритесь, подготовка PrintSream была не такой простой, может, мне вместо этого использовать фиктивный объект?

**EDIT**:
I think I should always сосредоточиться на интерфейсе. С моей точки зрения`setTarget()` не является частью "контракта" класса impl тоже нет, он служит для внедрения зависимостей. Я думаю, что любой открытый метод класса Impl должен рассматриваться как закрытый с точки зрения тестирования. Это не значит, что я игнорирую детали реализации.

Смотрите также[Должны ли частные / защищенные методы проходить модульное тестирование?](https://stackoverflow.com/questions/5601730/tdd-should-private-protected-methods-be-under-unit-test)

**EDIT-2** В случае нескольких реализаций \ нескольких интерфейсов я бы протестировал все реализации, но когда я объявил переменную в моем`setUp()` метод я бы определенно использовал интерфейс.

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

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

Да, вероятно, было бы проще использовать Mockito для PrintStream, но не всегда можно избежать использования фиктивного объекта, как вы делали в этом конкретном примере.



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

В конечном итоге вы тестируете реализацию.

В простом случае, как вы определили, я говорю шесть из полутора десятков других. Запишите свои тестовые примеры в интерфейс или реализацию, если он проверяетimplementation Достаточно, результаты одинаковы.

Возьмите другой пример, где у меня был бы класс, который собирает статистику по каналу связи, декорируя**real** читатель и писатель. Мой класс может теперь реализовать эти интерфейсы, но он также собирает статистику, которая не имеет ничего общего ни с одним контрактом. Конечно, я мог бы написать тесты на основе этих интерфейсов, но он не будет полностью тестировать этот класс.



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

Требование вызова сеттера в модульном тесте (приведение интерфейса к реализации):

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

Давайте возьмем пример из JDK. У вас есть интерфейс`List` и две реализации:`ArrayList` а также`LinkedList`, Дополнительно`LinkedList` инвентарь`Deque` интерфейс. Если вы пишете тест для`List` интерфейс, что бы вы покрыли? Массив или связанный список? Что еще в случае`LinkedList`какой интерфейс вы бы выбрали для тестирования?`Deque` или же`List`? Как вы видите, когда вы тестируете реализации, у вас нет таких проблем.

Лично для меня приведение интерфейса к реализации в модульном тесте является очевидным признаком того, что что-то идет не так;)



Я бы протестировал интерфейс.

Я думаю, что ошибка заключалась в написании реализации таким образом, что ее было сложно записать в System.out; Вы не дали себе возможности переопределить другой PrintStream. Я бы использовал конструктор вместо сеттера. Там нет необходимости издеваться или кастинг таким образом.

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

Прилипание к интерфейсу в ваших тестах также значительно облегчает насмешку.

Вот тест: