Bali
Bali

Reputation: 825

mocking external service with mockito

I have an application with following structure:

Client class:

class Client{
    private Requests requests;
    private Settings settings;
    protected Client(Settings settings){
        this.settings = settings;
        this.requests = new Requests(settings);
    }
    public Requests getRequests(){
        return requests;
    }
}

Requests class:

class Requests{
    private RequestsHandler requestsHandler;
    private Settings settings;

    protected Requests(Settings settings){
        this.requestHandler = new RequestHandler();
        this.settings = settings;
    }

    public Bicycles getBicycles(BicyclesParameters bicycleParams){
        return requestsHandler.callService(bicycleParams, settings, "service/getBicycles", Bicycles.class)
    }
}

And my

RequestsHandler class:

class RequestsHandler{
        public BaseResponse callService(BaseParams params, Settings settings, String serviceURL, Class<? extends BaseResponse> responseType ) {

            here I use RestTemplate to get data from external Service

        }
    }

In my Unit tests, I would like to mock the data from external service, so I did as following: create a mocking RequestsHandler object, mock the callService method and register this mock object with @Bean:

@Configuration
public class TestConfiguration {
    @Bean
    public RequestsHandler requestsHandler() {
        RequestsHandler requestsHandler = Mockito.mock(RequestsHandler.class);
        when(requestsHandler.callService(new BicyclesParameters(), new Settings(),
                "/service/getBicyles", Bicycles.class))
                        .thenReturn(new Bicycles().setBicyclesList(new Bicycle[] {new Bicycle(), new Bicycle()}));
        return requestsHandler;
    }
}

and my test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class TestGetBicycles{

    @Test
    public void testGetBicycles() {
        Client = new Client(new Settings);
        Bicycles bicycles = client.getRequests.getBicycles(new BicyclesParamters());
        assertNotNull(bicycles.getBicyclesList());
    }

}

But when I run the test, I had two problems:

  1. I become an

org.mockito.exceptions.misusing.InvalidUseOfMatchersException

, I have no idea why but when I changed my mock object to:

 when(requestsHandler.callService(any(), any(),
                any(), any()))
                        .thenReturn(new Bicycles().setBicyclesList(new Bicycle[] {new Bicycle(), new Bicycle()}));

the error went away, but

  1. the result I become is not the mock result, instead the result from the external service. This means the mock object was not taken into account.

Can anyone help me at these points? This is my first time working with Mockito so I am so confused now.

Thank you in advanced!

Upvotes: 2

Views: 5546

Answers (1)

davidxxx
davidxxx

Reputation: 131526

You create a mock but this is never used in the scope of the executed method.

In fact your problem is that you want to mock an object that is created inside a object created during the invocation of the tested method :

protected Requests(Settings settings){
    this.requestHandler = new RequestHandler(); // <- You want to mock this field
    this.settings = settings;
}

It is not possible with plain mockito. In your case you have two ways :

1) refactor your code to provide an additional constructor that accepts the RequestHandler as parameter. In your case Client and Requests could accept RequestHandler.
For example for Client :

protected Client(Settings settings, RequestsHandler requestHandler){
    this.settings = settings;
    this.requests = new Requests(settings, requestHandler);
}

and you test could look like :

@Mock
RequestsHandler requestsHandlerMock;
...
@Test
public void testGetBicycles() {
    Client = new Client(new Settings(), requestsHandlerMock);
    Bicycles bicycles = client.getRequests.getBicycles(new BicyclesParamters());
    //...
}

2) Using libraries that relies on reflection and byte code generation (as CGLib) at runtime. PowerMock and PowerMockito are well known in Java for that.

Personally I favor the refactoring as it is possible. It makes the code always clearer and cleaner. In fact these libraries are not bad in themselves but it may create some technical debt as by using them too often you may be encouraged to cheat with the help of the library instead of improving the API of the code to test.

Upvotes: 3

Related Questions