Как использовать mockito, чтобы не оценивать метод внутри метода тестирования


Я реализую тестовый пример для метода контроллера. Метод контроллера выглядит следующим образом:

public class LoginController{
   public String register(String token){
     //some logic 
     loginService.delete(String token);
    //some logic
   return "xxxx";
   }
}

Я реализую тестовый пример для проверки метода регистрации и не хочу, чтобы оценивался метод удаления. (Метод удаления — это служебный метод, который возвращает пустоту). Я провел небольшое исследование и использовал приведенный ниже код в своем тестовом методе, чтобы не оценивать метод удаления, но все же, когда я отлаживаю, он входит в метод удаления. Может ли кто-нибудь указать мне, что я сделал неправильно.

public class LoginControllerTest{
   private loginService loginServiceMock;

   @Test
   public void testRegister(){
      loginServiceMock = new loginServiceImpl();
      loginService spy = spy(loginServiceMock);
      doNothing().when(spy).delete(any(String.class));
      //calling the controller method 
   }
}

person Anna    schedule 23.08.2016    source источник
comment
кстати. в org.mockito.Matchers есть сопоставитель anyString(). Используйте это вместо любого (String.class). Чище и быстрее. Вы можете проверить в исходном коде, что returnString() намного проще, чем returnFor(Class‹T›clazz) в org.mockito.internal.progress.HandyReturnValues   -  person Sarseth    schedule 23.08.2016
comment
@ Анна, метод delete класса LoginService окончательный? Или сам класс final? Любое из этих двух условий предотвратит заглушение этого метода так, как вы намереваетесь.   -  person Dawood ibn Kareem    schedule 23.08.2016
comment
@DavidWallace Нет, ни один из них не является окончательным.   -  person Anna    schedule 23.08.2016
comment
Хорошо, как вы внедряете свой шпионский объект в LoginController? Не могли бы вы показать часть теста, где создается LoginController и к нему добавляется LoginService?   -  person Dawood ibn Kareem    schedule 23.08.2016
comment
Посмотрите на метод never() в Mockito   -  person    schedule 23.08.2016


Ответы (3)


То, что вы делаете, должно работать до тех пор, пока loginService spy объект вводится в LoginController, который вы тестируете. Это не видно из кода, который вы разместили.

Есть 2 причины, по которым контроллер вызывает метод службы реального входа в систему:

  1. Вы забыли ввести шпиона в контроллер: что-то вроде loginControllerToTest = new LoginController(spy) или loginControllerToTest.setLoginService(spy).

  2. loginService.delete() — это статический метод, и в этом случае вам нужно либо реорганизовать свой код, чтобы удалить статическую зависимость, либо использовать другой инструмент для имитации, такой как powermock. Подробнее см. этот вопрос.

person noscreenname    schedule 23.08.2016

Рефакторинг LoginController во что-то вроде

public class LoginController {
    private LoginService loginService;

    public LoginController(LoginService loginService) {
        this.loginService = loginService;
    }

    public String register(String token){
        //some logic 
        loginService.delete(token);
        //some logic
        return "xxxx";
    }
 }

 public interface LoginService {
     void delete(String token);
 }

И тогда в вашем тесте

public class LoginControllerTest {
   private LoginController loginController;

   @Test
   public void testRegister(){
      loginController = new LoginController(t -> {});

      loginController.register("foo");

      //do some assertion
   }
}

Я знаю, что это не то решение, которое вы (возможно) ожидали бы, но оно решает вашу проблему (настоящее delete больше не называется).

Другие преимущества этого решения:

  • Код более развязан, более удобен в сопровождении
  • Прямое следствие вышеизложенного: код становится легче тестировать.
  • Прямое следствие вышеизложенного: вам больше не нужно делать сложные вещи, требующие макета.
person Spotted    schedule 23.08.2016

Вся идея spy заключается в том, что он позволяет вам вызывать и проверять методы на реальных экземплярах. Поэтому, если вы вызываете метод шпионского экземпляра, он фактически вызывает метод для фактического экземпляра, если только он не смоделирован.

В вашем случае вам нужно использовать mock вместо spy:

@RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest{

   @InjectMocks
   private LoginController controller;

   @Mock
   private loginService loginServiceMock; 


   @Test
   public void testRegister(){      

      doNothing().when(loginServiceMock).delete(anyString()));
      //calling the controller method 
      String value = controller.register("mytoken");
      verify(loginServiceMock,times(1)).delete(anyString());
   }
}
person kuhajeyan    schedule 23.08.2016
comment
Я пробовал, но при тестировании метода регистрации все равно уходит внутрь метода удаления :( - person Anna; 23.08.2016
comment
@Анна, можете ли вы обновить свой полный тестовый класс, и вы аннотировали свой тестовый класс с помощью @RunWith(MockitoJUnitRunner.class)? - person kuhajeyan; 23.08.2016
comment
да. Другие мои тесты проходят нормально. Класс довольно большой, поэтому я упростил и добавил только необходимые фрагменты кода. - person Anna; 23.08.2016