I am trying to run a test. Where I try to mock a specific class. So that you understand the scenario we have a class Controller, Service, Dao, and finally the class in question that I am trying to mock.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Main.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
@ActiveProfiles("api")
public class UnitTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
private static final String API_URL = "http://test:6536/api/url";
private static final String DATA_CONSTANT = "{\"nationality\":\"Camboya\"}";
private static final Bean BEAN = BeanBuilder.getInstance().withId("").withData(DATA_CONSTANT).build();
@Mock
private HttpIntegrator httpIntegrator; //La clase que despues de surcar controlador, servicio y dao hace la llamada y quiero moquear
@Autowired
private ApiController controller; //La clase la cual quiero que coja del contexto de spring para asi poder alcanzar todas las clases que son inyectadas(tales como servicio, dao etc)
@Test
public void post_Ok() throws Exception {
final HttpResponseBean httpResponse= HttpResponseBean.newInstance(HttpStatus.OK.ordinal(), "OK", BEAN.toString().getBytes());
Mockito
.doReturn(httpResponse)
.when(this.httpIntegrator).postRequest(Matchers.any(String.class), Matchers.any(Map.class), Matchers.any(String.class));
final ResponseEntity<Response> response = controller.post(DATA_CONSTANT);
Mockito.verify(this.httpIntegrator).postRequest(API_URL, generateHttpRequestHeaders(), DATA_CONSTANT); //Excepcion
}
}
I load the SpringRunner runner so I can take advantage of the classes it injects into the context when the app starts. Since I'm not interested in mocking all classes. I'm just trying to sniff HttpIntegrator. But of course to be able to mock I need the mockito runner, so I have added the rule to be able to add it.
I mock HttpIntegrator and set the behavior I want when it gets called. Debugging I'm already annoyed that by putting a breakpoint in the method I call from HttpIntegrator I can perform the debug (I say I'm annoyed because I thought mocks couldn't be debugged, but I'm probably wrong).
The issue is that when I check if it has made the call I get the following exception and that is what really assures me that it does not work.
Wanted but not invoked
What I can be doing wrong? Thanks in advance.
UPDATE
The response I get is not the same as the one I apply in doReturn(). Since I have defined the response of the mock with the following:
final HttpResponseBean httpResponse= HttpResponseBean.newInstance(HttpStatus.OK.ordinal(), "OK", BEAN.toString().getBytes());
But what it returns to me does not correspond to that. It is similar, but the BEAN.toString() that the mocked method returns to me is the one that returns me as if I made the call without having established the response that the mock should have. So I have established a series of matchers to the matcher and the error that returns me is the following:
Mockito.verify(this.httpIntegrator).postRequest(API_URL, Matchers.anyObject(), Matchers.anyString());
invalid use of argument matchers 3 matchers expected 2 recorded
I am currently looking for the cause
By using
@Mock
only you get to create a mock class forHttpIntegrator
, you are not injecting it into the Dao (by using @AutowiredApiController
you are delegating dependency injection to Spring) To fix this, it is easiest to annotate HttpIntegrator with@MockBean
instead of with@Mock
@MockBean
creates the mock class and also replaces any bean that has been defined in the Spring context with your mock class.The "invalid use of argument matchers 3 matchers expected 2 recorded" error is because in the call to
verify
you are indicating twoarguments matchers
, and once you have included one, all parameters must be as well. That is,API_URL
it cannot go together with the other two parameters. Try changing it toMatchers.equalTo(API_URL)