Anna
Anna

Reputation: 1649

How to use mockito to not evaluate a method inside the testing method


I am implementing a test case for a controller method. The controller method looks like below,

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

I am implementing the test case to test the register method and i do not want the method delete to be evaluated. (The delete method is a service method that returns a void). I did a bit of research and used the below code in my test method to not evaluate the delete method, but still when i debug it goes inside the delete method. Can anyone point put me out what wrong I have done.

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 
   }
}

Upvotes: 2

Views: 2877

Answers (3)

kuhajeyan
kuhajeyan

Reputation: 11017

Whole idea of spy is, it allows you to invoke and verify methods on actual instances. So if you invoke a spied instance method it actually invokes the method on actual instance unless it is mocked.

For your case you need to use mock instead of 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());
   }
}

Upvotes: 0

noscreenname
noscreenname

Reputation: 3370

What you are doing should work as long as loginService spy object is injected into LoginController that you are testing. This is not visible from the code you've posted.

There are 2 reasons that would make the controller call the real login service method :

  1. You forgot to inject the spy into the controller: something like loginControllerToTest = new LoginController(spy) or loginControllerToTest.setLoginService(spy).

  2. loginService.delete() is a static method in which case you need to either refactor your code to remove static dependency or use a different mocking tool such as powermock. See this question for details.

Upvotes: 0

Spotted
Spotted

Reputation: 4091

Refactor LoginController to something like

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);
 }

And then in your test

public class LoginControllerTest {
   private LoginController loginController;

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

      loginController.register("foo");

      //do some assertion
   }
}

I know that's not the kind of solution you (maybe) would have expected but it solves your problem (the real delete is not called anymore).

Other advantages with this solution:

  • The code is more decoupled, more maintainable
  • Direct consequence of above: the code becomes easier to test
  • Direct consequence of above: you don't need to do complicated stuff that requires a mock anymore

Upvotes: 1

Related Questions