Как имитировать вызов атрибута класса?

#java #unit-testing #mockito

Вопрос:

Я пытаюсь протестировать следующий класс

 public class MyClass {
    protected OtherClass other;
    public MyClass(String arg) {
        this.other = new OtherClass(arg);
    }

    public void doSomething() {
        String response = other.sendRequest(); // mock this call
        // ...
    }
}
 

Я хочу проверить doSomething() , и для этого мне нужно издеваться над вызовом sendRequest() . Для этого я попробовал следующее, что не сработало:

 @Test
public void doSomethingTest() {
    MyClass myClass = new MyClass("test");
    when(myClass.other.sendRequest()).thenReturn("response");
    myClass.doSomething();
}
 

Реальный sendRequest() все еще вызывается вместо того, чтобы возвращать поддельный ответ.

Как я могу издеваться над вызовом sendRequest() с помощью Mockito?

Комментарии:

1. Вам нужно ввести объект OtherClass в MyClass (например, с помощью конструктора). Если вы создадите экземпляр объекта OtherClass в классе MyClass, то на самом деле невозможно издеваться над ним

2. Классический случай внедрения зависимостей. В исходном коде объект, используемый для вызова sendRequest, создается снаружи. Ваш тест никогда не сможет получить доступ к этому объекту. Вы создаете макет, но этот макет объекта никогда не вызывается, потому что класс Myclass даже не принимает макет. Но если вы отправите созданный объект pbject в Myclass, то в вашем тесте вы можете заменить другой объект класса объектом, созданным в вашем тесте. Вот как инъекция зависимостей помогает в тестах. Это позволяет u заменить объект, который будет использоваться в классе.

Ответ №1:

Если вы создаете экземпляр OtherClass объекта в MyClass классе, то на самом деле невозможно издеваться над ним, вам нужно создать его вне MyClass , а затем внедрить в MyClass него (например, через его конструктор).

Тогда все, что вам нужно сделать в своем тесте, — это имитировать вызов макетного OtherClass объекта. И используйте его для создания экземпляра MyClass объекта

Измените MyClasss, чтобы быть

 public class MyClass {

    protected OtherClass other;
    
    public MyClass(OtherClass otherClass) {
        this.other = otherClass;
    }

    public void doSomething() {
        String response = other.sendRequest(); 
    }

}
 

и ваш код изменится с

         MyClass myClass = new MyClass("s");
 

Для

         OtherClass other = new OtherClass("s");
        MyClass myClass = new MyClass(other);
 

И теперь вы можете написать этот тест:

 @Test
public void doSomethingTest() {
    OtherClass other = mock(OtherClass.class);
    when(other.sendRequest()).thenReturn("response");
    
    MyClass myClass = new MyClass(other);
    myClass.doSomething();

   // .. test assertions
}
 

Комментарии:

1. Возможно, вам не придется менять свой класс MyClass. Вы могли бы наследовать от него в своем тесте по sth. например, MyTestClass расширяет MyClass{ MyTestClass(строка s){другое =макет(OtherClass.class); когда(other.sendRequest()).Затем вернитесь(«ответ»); } } и затем вы протестируете с помощью MyClass MyClass = новый MyTestClass(«s»);

2. @juwil Это звучит плохо для меня, это не то, для чего предназначено наследование. Другим способом может быть наличие защищенного конструктора (следовательно, доступного из теста), который принимает объект OtherClass