Я тестирую микросервис Spring Boot.Я хочу использовать Mockito, чтобы вызвать исключение при вызове метода в определенном процессе, чтобы я мог улучшить покрытие модульных тестов.Проблема в том, что этот метод (назовем его doB()
) вызывается объектом, который существует только в локальной области действия метода (B
), созданным статическим конечным объектом фабрики (A
).
Я хочу сделать что-то вроде:
doThrow(new RuntimeException()).when(mockedB).doB(anyString());
, чтобы я мог утверждать, что fooResult
возвращает null
и, следовательно, улучшить покрытие тестом, покрывая случай исключения catch.
Но возможно ли вообще создать mockedB
полезным способом?Если да, то как?
Я пробовал различные комбинации mock()
и spy()
, правда, с небольшим успехом.Я новичок в использовании Mockito, но я почти уверен, что суть проблемы в том, что если я просто высмею Foo, то я не смогу видеть A
и B
внутри, что-то делать, но пытатьсяложный или шпионский A
или B
не работает, так как они не совпадают с A
или B
, созданными внутри Foo
.A
, будучи финальным и статичным, вероятно, и здесь не дает мне никакой пользы.
В качестве примера я удалил почти все, кроме самой необходимой функциональности.Класс, который я тестирую, это Foo
, который использует A
и B
для внутреннего использования.
Вот Foo
:
public class Foo {
private static final A localA = new A();
public FooResult doFoo(String fooString) {
try {
B localB = localA.createB();
return localB.doB(fooString);
} catch (RuntimeException e) {
//exception handling here
}
return null;
}
}
И это A
:
public class A {
//unimportant internal details
private Object property;
public A() {
this(null);
}
public A(Object property) {
this.property = property;
}
public B createB() {
//assume for sake of example that this constructor
//is not easily accessible to classes other than A
return new B();
}
}
А теперь B
:
public class B {
public FooResult doB(String str) throws RuntimeException {
//lots of processing, yada yada...
//assume this exception is difficult to trigger just
//from input due to details out of our control
return new FooResult(str);
}
}
Вот FooResult
public class FooResult {
private String fooString;
public FooResult(String str) {
this.fooString = str;
}
public String getFooString() {
return fooString;
}
}
Наконец, вот тест:
@RunWith(PowerMockRunner.class)
public class FooTest {
@InjectMocks
Foo foo;
@Test
public void testDoFoo() {
String fooString = "Hello Foo";
FooResult fooResult = foo.doFoo(fooString);
assertEquals(fooResult.getFooString(), fooString);
//works fine, nothing special here
}
@Test
@PrepareForTest({Foo.class, A.class, B.class})
public void testDoFooException() throws Exception {
//magic goes here???
A mockedA = PowerMockito.mock(A.class);
B mockedB = PowerMockito.mock(B.class);
PowerMockito.when(mockedA.createB()).thenReturn(mockedB);
PowerMockito.doThrow(new RuntimeException()).when(mockedB).doB(Mockito.anyString());
FooResult fooResult = foo.doFoo("Hello Foo");
//expect doFoo() to fail and return null
assertNull(fooResult);
}
}
Как я уже говорил ранее, я ожидаю, что макет сработает при вызове doB()
, в результате чего doB()
вернет null
.Это не работает, и исключение не выбрасывается.
У меня такое чувство, что пытаться делать это - плохая практика.Возможно, лучшим способом было бы изменить метод так, чтобы я мог вместо этого передать свой собственный A
объект, чтобы я мог наблюдать его.Но давайте просто скажем, что я не могу изменить любой исходный код.Это вообще возможно?