Предположим, что у меня есть интерфейс и класс реализации, который его реализует, и я хочу написать unit-test для этого. Что я должен проверить интерфейс или 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()
не является частью моего открытого интерфейса, поэтому я не хочу случайно его называть. Если я действительно хочу это назвать, я должен подумать об этом. Это помогает мне, например, обнаружить, что я действительно пытаюсь сделать инъекцию зависимости. Это открывает для меня весь мир новых возможностей. Я могу использовать какой-то существующий механизм инъекции зависимостей (например, Spring), я могу имитировать его сам, как это было на самом деле в моем коде, или использовать совершенно другой подход. Присмотритесь, подготовка PrintSream не так-то просто, может быть, я должен использовать mock-объект вместо этого?
ИЗМЕНИТЬ:
Я думаю, что я всегда должен сосредоточиться на интерфейсе. С моей точки зрения, setTarget()
не является частью "контракта" класса impl, и он служит для инъекции зависимостей. Я думаю, что любой публичный метод класса Impl следует считать приватным с точки зрения тестирования. Это не означает, что я игнорирую детали реализации.
См. также Должны ли частные/защищенные методы находиться под unit test?
EDIT-2 В случае нескольких реализаций\нескольких интерфейсов я бы тестировал все реализации, но когда я объявляю переменную в моем методе setUp()
, я бы определенно использовал интерфейс.