Модульное тестирование с помощью mockito для конструкторов
У меня есть один класс.
Class First {
private Second second;
public First(int num, String str) {
second = new Second(str);
this.num = num;
}
... // some other methods
}
Я хочу написать модульные тесты для общедоступных методов класса First. Я хочу избежать выполнения конструктора класса Second.
Я сделал это:
Second second = Mockito.mock(Second.class);
Mockito.when(new Second(any(String.class))).thenReturn(null);
First first = new First(null, null);
Он по-прежнему вызывает конструктор класса Second. Как я могу избежать этого?
Ответы
Ответ 1
Снова проблема с модульным тестированием происходит от создания вручную объектов с помощью оператора new
. Рассмотрим вместо этого уже созданную Second
:
class First {
private Second second;
public First(int num, Second second) {
this.second = second;
this.num = num;
}
// some other methods...
}
Я знаю, что это может означать серьезную переписку вашего API, но другого выхода нет. Также этот класс не имеет никакого смысла:
Mockito.when(new Second(any(String.class).thenReturn(null)));
Прежде всего, Mockito может только издеваться над методами, а не с конструкторами. Во-вторых, даже если вы можете издеваться над конструктором, вы издеваетесь над конструктором только что созданного объекта и никогда ничего не делаете с этим объектом.
Ответ 2
Вы можете использовать PowerMockito
См. пример:
Second second = Mockito.mock(Second.class);
whenNew(Second.class).withNoArguments().thenReturn(second);
Но рефакторинг - лучшее решение.
Ответ 3
Вот код, чтобы высмеять эту функциональность с помощью PowerMockito API.
Second mockedSecond = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mockedSecond);
Вам нужно использовать бегун Powermockito и добавить необходимые классы тестов (разделенные запятыми), которые должны быть издеваемы API-интерфейсом powermock.
@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class,Second.class})
class TestClassName{
// your testing code
}
Ответ 4
Я использовал "Pattern 2 -" factory вспомогательный шаблон "
Образец 2 - вспомогательный шаблон factory
В одном случае, когда этот шаблон не будет работать, если MyClass является окончательным. Большинство рамок Mockito не особенно хорошо сочетаются с финальными классами; и это включает использование spy(). Другой случай, когда MyClass использует getClass() где-то, и требует, чтобы полученное значение было MyClass. Это не сработает, потому что класс шпиона - фактически подкласс Mockito для исходного класса.
В любом из этих случаев вам понадобится немного более надежный factory вспомогательный шаблон, как показано ниже.
public class MyClass{
static class FactoryHelper{
Foo makeFoo( A a, B b, C c ){
return new Foo( a, b, c );
}
}
//...
private FactoryHelper helper;
public MyClass( X x, Y y ){
this( x, y, new FactoryHelper());
}
MyClass( X x, Y, y, FactoryHelper helper ){
//...
this.helper = helper;
}
//...
Foo foo = helper.makeFoo( a, b, c );
}
Итак, у вас есть специальный конструктор, только для тестирования, который имеет дополнительный аргумент. Это используется из вашего тестового класса при создании объекта, который вы собираетесь тестировать. В вашем тестовом классе вы издеваетесь над классом FactoryHelper, а также с объектом, который хотите создать.
@Mock private MyClass.FactoryHelper mockFactoryHelper;
@Mock private Foo mockFoo;
private MyClass toTest;
и вы можете использовать его следующим образом
toTest = new MyClass( x, y, mockFactoryHelper );
when( mockFactoryHelper.makeFoo(
any( A.class ), any( B.class ), any( C.class )))
.thenReturn( mockFoo );
Источник: http://web.archive.org/web/20160322155004/http://code.google.com/p/mockito/wiki/MockingObjectCreation
Ответ 5
Я считаю, что невозможно издеваться над конструкторами, использующими mockito. Вместо этого я предлагаю следующий подход
Class First {
private Second second;
public First(int num, String str) {
if(second== null){
//when junit runs, you get the mocked object(not null), hence don't
//initialize
second = new Second(str);
}
this.num = num;
}
... // some other methods
}
class TestFirst{
@InjectMock
First first;//inject mock the real testable class
@Mock
Second second
testMethod(){
//now you can play around with any method of the Second class using its
//mocked object(second),like:
when(second.getSomething(String.class)).thenReturn(null);
}