Это, наверное, простой вопрос, но я не смог его укоротить.
Я тестирую мой класс ClassToTest
. В процессе производства он будет выполнять операции над сторонним библиотечным объектом, экземпляром ThirdPartyClass
.
Я хочу подражать этому классу с заглушкой. (Используя определение заглушки, используемое эссе Мартина Фаулера: Stubs предоставляют консервированные ответы на вызовы, сделанные во время теста, обычно вообще не реагируют на что-либо вне того, что запрограммировано для теста)
Проблема в:
ClassToTest
имеет метод setThirdPartyClass(ThirdPartyClass o)
. Таким образом, чтобы пропустить это, заглушке пришлось бы расширить ThirdPartyClass
. Это означает, что мои тесты должны будут использовать стороннюю библиотеку, тогда как мой unit тест должен быть автономным. Причина в том, что мой код будет запущен во встроенной системе, а соответствующая библиотека в основном выполняет собственный код. Но я хочу разработать и протестировать свой код изолированно на своем ПК. Но я не хочу изменять подписи аргументов ClassUnderTest
и т.д., Например, меняя между собой:
classToTest.setThirdPartyClass(ThirdPartyClass o){ /*....*/ }
а также
classToTest.setThirdPartyClass(MyThirdPartyClassStub o){ /*....*/ }
AFAIK, обычно заглушка просто расширяет класс, который он имитирует, поэтому подписи не нужно менять (и переопределять конструкторы и т.д. С минимальным кодом).
Каков хороший способ сделать это?
Возможно, это не лучший пример, но, по существу, использование Mockito для издевательства над ThirdPartyClass.
YourClass
для тестирования
public class YourClass {
private String aString;
// Method that depends on third party component
public void someMethod(ThirdPartyClass doc) {
aString = doc.someStringValue();
}
public String getValue() {
return aString;
}
}
Протестируйте с помощью Mockito, чтобы высмеивать ThirdPartyClass
и контролировать возвращаемое значение
public class YourClassTest {
//Class you want to test
private YourClass testee;
@Test
public void testSomeMethod() {
testee = new YourClass();
//
// ThirdPartyClass. When the method someStringValue()
// is called you control the value returned
//
ThirdPartyClass testDoc = mock(ThirdPartyClass.class);
when(testDoc.someStringValue()).thenReturn("aString");
// Call method to test and pass mock as argument
testee.someMethod(testDoc);
// Check state of your object you want to test
assertThat(testee.getValue(), equalTo("aString"));
}
}
Самый простой способ издевательства (ThirdPartyClass
) ThirdPartyClass
, как уже упоминалось, будет, если этот класс реализует интерфейс, а этот интерфейс - это полный интерфейс, ClassToTest
использует ваш ClassToTest
. Затем вы можете написать класс MockThirdParty
который implements ThirdPartyInterface
. Но я думаю, вы не можете этого сделать.
Другой вариант - скрыть ThirdPartyClass
за классом ThirdPartyFacade
который вы пишете. Когда вы пишете это, вы можете создать ThirdPartyInterface
который вы создаете. Методы класса ThirdPartyFacade
просто делегируют ThirdPartyClass
, поэтому для вас безопасно проводить сравнительно небольшое тестирование этого класса; модульные тесты для этого не нужны. Измените свой ClassToTest
чтобы он использовал ссылку на объект ThirdPartyInterface
, а не ссылку на объект ThirdPartyClass
.
Теперь вы можете написать класс MockThirdPartyFacade
для тестирования, который implements ThirdPartyInterface
для тестирования ClassToTest
. Вы можете сделать этот макет класс простым или сложным, как вам нужно. Различные тестовые примеры могут даже использовать разные классы-макеты; вам не нужен класс общей цели.
ThridPartyClass
ли ваш ThridPartyClass
интерфейс? Если это так, вы можете изменить свой класс под тестом, чтобы использовать интерфейс вместо конкретного класса. Тогда ваш заглушка просто реализует интерфейс.
Если нет, есть издевательские библиотеки, такие как EasyMock и Mockito, которые могут издеваться над классом для вас.
Например, используя easymock:
@Test
public void test(){
ThirdPartyClass mock = createMock(ThirdPartyClass.class);
expect(mock.foo().andReturn("bar");
replay(mock);
classToTest.setThirdPartyClass(mock);
//perform your tests on classToTest
}