Тестируемый модуль: 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()
метод я бы определенно использовал интерфейс.