Jukito/Mockito Test статическим методом
Я пытаюсь протестировать класс (используя Jukito и Mockito), который, к сожалению, расширяет другой класс, который имеет статический вызов метода. Можно ли как-то пропустить этот звонок? Я бы предпочел не использовать PowerMockito.
public class A extends B {
@Inject
public A(final String s){
super(s);
}
}
public abstract class B {
private String s;
protected String m = C.get().createUniqueId(); //Exception is thrown here
public B(String s){
this.s = s;
}
}
public class C {
private static C c; //assume this is never null
public static C get() {
return c;
}
public final native String createUniqueId() {}
}
@RunWith(JukitoRunner.class)
public class ATest {
@Inject
A a;
@Test
public void onMethod1Test(){
}
}
При запуске ATest я получаю следующую ошибку:
Error injecting constructor, java.lang.UnsatisfiedLinkError: C
Я предположил, что это из-за статического метода, я был неправ?
Обратите внимание, что все классы являются лишь примерами из моих реальных классов, и класс C не был написан моим и не может быть изменен (к сожалению). Но идея моих занятий одинакова, я просто сменил имена и оставил только соответствующие части.
2 ответа
Джукито утверждает:
Совокупная мощь JUnit, Guice и Mockito.
Но дело в том, что ни один из этих продуктов не позволяет имитировать статические методы.
Единственные фреймворки, способные на это: PowerMock(ito) и JMockit.
Как вы уже объясняли: обычно вы бы "обошли" этот "недостаток", просто написав тестируемый код (который избегает статических вызовов). Но так как вы не можете улучшить свой дизайн, у вас есть только два варианта: использовать PowerMock (ito) для тестирования этого класса или не тестировать его.
Таким образом, цель состоит в том, чтобы использовать C
чтобы генерировать m
на любом новом экземпляре B
, Вы не контролируете C
и вы пытаетесь понять, как это проверить, верно? Я думаю, вам придется выбрать яд, но я могу придумать другой вариант "яда" для вашей ситуации.
Вы добавляете статическое поле на B
и дать ему больше доступа, чем было бы уместно:
public abstract class B {
static C c = C.get();
private String s;
protected String m = c.createUniqueId();
public B(String s){
this.s = s;
}
}
Теперь вы можете переназначить B.c
на осмеянный экземпляр в вашем тесте. Я больше знаком с JUnit и Spock, поэтому я постараюсь, чтобы вы могли разобраться в механике этого. Поскольку модульные тесты находятся в том же пакете, что и класс, который они тестируют, вы можете использовать закрытую область пакета. Если A
находится в другой упаковке, чем B
тогда вам придется продвигать его protected
, Это быстро и просто, но предоставляет вам возможность переназначения другого кода B.c
, Вы должны были бы оценить этот риск по сравнению с риском не тестировать A
совсем.
Вы также можете добавить классы и интерфейсы, чтобы скрыть C
от A
а также B
в целом. По сути, вы создаете что-то вроде BFactory
который имеет Supplier<String>
чтобы генерировать m
, В модульных тестах вы издеваетесь над поставщиком, а в производстве вы используете реализацию, основанную на C
, Каждый способ, которым я могу думать, является грязным или не заставляет каждый подкласс B
чтобы генерировать m
таким же образом. Единственное исключение будет, если на самом деле имеет больше смысла использовать композицию и поместить экземпляр B
на A
, Тогда у вас может быть приличный способ сделать это.