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, Тогда у вас может быть приличный способ сделать это.

Другие вопросы по тегам