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

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

Вот пример:

public interface HelloInterface {
    public void sayHello();
}


public class HelloInterfaceImpl implements HelloInterface {
    private PrintStream target = System.out;


    @Override
    public void sayHello() {
        target.print("Hello World");

    }

    public void setTarget(PrintStream target){
        this.target = target;
    }
}

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

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

public class HelloInterfaceTest {
    private HelloInterface hi;

    @Before
    public void setUp() {
        hi = new HelloInterfaceImpl();
    }

    @Test
    public void testDefaultBehaviourEndsNormally() {
        hi.sayHello();
        // no NullPointerException here
    }

    @Test
    public void testCheckHelloWorld() throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream target = new PrintStream(out);
        PrivilegedAccessor.setValue(hi, "target", target);
        //You can use ReflectionTestUtils in place of PrivilegedAccessor
        //really it is DI 
        //((HelloInterfaceImpl)hi).setTarget(target);
        hi.sayHello();
        String result = out.toString();
        assertEquals("Hello World", result);

    }
 }

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

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

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

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

Смотрите такжеДолжны ли частные / защищенные методы проходить модульное тестирование?

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

Ответы на вопрос(4)

Ваш ответ на вопрос