Test method with okhttp3 call - java

I have a method in the service class that makes a call to an external api. How would I mock this okHttpClient call? I have tried to do so with mockito but no luck with that.
//this is the format of the method that i want to test
public string sendMess(EventObj event) {
OkHttpClient client = new OkHttpClient();
//build payload using the information stored in the payload object
ResponseBody body =
RequestBody.create(MediaType.parse("application/json"), payload);
Request request = //built using the Requestbody
//trying to mock a response from execute
Response response = client.newCall(request).execute();
//other logic
}
I am open to refactoring the service class if it helps with the testing. Any suggestions and recommendation is appreciated. Thanks.

Since you are using spring-boot leave managing beans to spring.
1) First create OkHttpClient as spring bean so that you can use it all over application
#Configuration
public class Config {
#Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
2) And then in the service class #Autowire OkHttpClient and use it
#Service
public class SendMsgService {
#Autowired
private OkHttpClient okHttpClient;
public string sendMess(EventObj event) {
ResponseBody body = RequestBody.create(MediaType.parse("application/json"), payload);
Request request = //built using the Requestbody
//trying to mock a response from execute
Response response = okHttpClient.newCall(request).execute();
//other logic
}
}
Tests
3) Now in the Test classes use #SpringBootTest,#RunWith(SpringRunner.class) and #MockBean
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
#RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in out JUnit tests, this annotation will be required.
#MockBean Annotation that can be used to add mocks to a Spring ApplicationContext.
#SpringBootTest
#RunWith(SpringRunner.class)
public class ServiceTest {
#Autowire
private SendMsgService sendMsgService;
#MockBean
private OkHttpClient okHttpClient;
#Test
public void testSendMsg(){
given(this.okHttpClient.newCall(ArgumentMatchers.any())
.execute()).willReturn(String);
EventObj event = //event object
String result = sendMsgService.sendMess(event);
}
}

I would suggest, that you pull out the instantiation of your OkHttpClient to an own method in a Configuration class. Afterwards you could #Inject the client anywhere it is needed and testing becomes much easier because you can #Mock it away.
Such to say the Spring-managed bean:
#Configuration
public class OkHttpClientConfiguration {
#Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
…your production class:
#Component
public class ProductionClass {
#Inject
private OkHttpClient okHttpClient;
public string sendMess(EventObj event) {
okHttpClient // whatever you want
[…]
}
}
…and your test:
public class SpyTest {
#InjectMocks
private ProductionClass productionClass;
#Mock
private OkHttpClient okHttpClient;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void spyInsteadOfPowermock() {
Request request = // mock the request
when(okHttpClient.newCall(request)).thenReturn(mock(Call.class));
}
}

Related

Unable to mock ReactriveJWTDecoder

Given I have the following custom implementation of ReactiveJwtDecoder:
#Component
public class CustomReactiveJWTDecoder implements ReactiveJwtDecoder
And I write a test like:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AzureADConfigTest {
#Autowired
private ApplicationContext context;
private WebTestClient client;
#MockBean
AzureADService azureADService;
#MockBean
CustomReactiveJWTDecoder decoder;
#Before
public void setUp() {
Jwt jwt = createJwt();
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(new User("test#user.se", "", AuthorityUtils.commaSeparatedStringToAuthorityList("A_AD")), "", AuthorityUtils.commaSeparatedStringToAuthorityList("A_AD"));
when(decoder.decode(anyString())).thenReturn(Mono.just(jwt));
when(azureADService.getUserInfo(any())).thenReturn(Mono.empty());
client = WebTestClient.bindToApplicationContext(context).build();
}
#Test
public void azureAdAccessGrated() {
client
.get()
.uri("/api/userinfo")
.header("Authorization", "Bearer " + "token")
.exchange()
.expectStatus()
.isOk();
}
}
The mock is not respected. If I put a breakpoint inside my original impl that code gets executed instead of the mock.
My question is:
How can I mock a ReactiveJWTDecoder when im using a Spring Boot Security Resource Server? There are many suggestions on how to do it but none that I as simple as just creating a #MockBean.

Mock Feign client in Spring controller in integration test

I have a gateway controller that uses internally different services. I try to write integration test that mock feign client for the controller but it doesn't work as I expected.
I have the following Feign client:
public interface StoreManagementClient {
#RequestLine("GET /v1/stores/{storeId}")
#Headers({"Accept: application/json", "Content-Type: application/json;charset=UTF-8"})
StoreDetails getStoreDetails(#Param("storeId") String storeId);
}
Store controller:
#Validated
#Controller
#RequestMapping("${gateway.path}")
public class StoreController {
#Autowired
private StoreManagementClient storeManagementClient;
#GetMapping(value = "/stores/{storeId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<StoreDetails> getStoreDetails(
#PathVariable("storeId") String storeId) {
StoreDetails details = storeManagementClient.getStoreDetails(storeId);
return ResponseEntity.ok(details);
}
}
And the integration test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {GatewayServiceApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class ClientIntegrationTest {
#Autowired
private StoreController storeController;
#MockBean
private StoreManagementClient storeManagementClient;
private MockClient mockClient;
#Before
public void setUp() throws Exception {
mockClient = new MockClient();
}
#Test
public void testCorrectGetStoreDetailsRequest() throws JsonProcessingException {
String storeId = "store-1";
StoreDetails storeDetails = new StoreDetails();
storeDetails.setId(storeId);
storeDetails.setType("grocery");
String response = new ObjectMapper().writeValueAsString(storeDetails);
storeManagementClient = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.client(mockClient
.ok(RequestKey.builder(feign.mock.HttpMethod.GET, "/v1/stores/" + sroreId)
.headers(ImmutableMap.of(
ACCEPT, newArrayList("application/json"),
CONTENT_TYPE, newArrayList("application/json;charset=UTF-8"))).build(),
response
))
.target(new MockTarget<>(StoreManagementClient.class));
// when
ResponseEntity<StoreDetails> result = storeController.getStoreDetails(storeId);
// then
StoreDetails resultBody = result.getBody();
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(resultBody.getId()).isEqualTo(storeId);
assertThat(resultBody.getType()).isEqualTo("grocery");
}
I suppose that test should mock response according the described Feign Client. But actually it returns null.
Should I do something wrong with mocking Feign client? Probably, I mixed in one test testing Feign client and my own controller and I need to separate it and write unit-test for Feign client like Mock Feign Client example?
I would be appreciate any advice
First, you replace feign client StoreManagementClient by mock:
#MockBean
private StoreManagementClient storeManagementClient;
Then in test you lost reference to the mock and points to local object:
storeManagementClient = Feign.builder()....build();
but controller still use the mock
In Plain java, you do something like:
Client client = new Client(null);
Controller controller = new Controller(client);
client = new Client(1);
assertThat(controller.get()).isEqualTo(1)) // no no no: is equal to null
PS. I hope in future my answer will be constructive

Unit test service class with mocks using TestNG

I am attempting to write a unit test for a generic service class like the following:
public class ApiService{
private RestTemplate restTemplate;
private ServiceDao serviceDao;
#Autowired
public ApiService(RestTemplate restTemplate, ServiceDao serviceDao) {
this.restTemplate = restTemplate;
this.serviceDao = serviceDao;
}
public ResponseEntity getObject(ObjectRequest request) {
// Service logic here
}
public ResponseEntity postObject(CreateObjectRequest request) {
// Service logic here
}
}
But am struggling with how to mock the restTemplate in the constructor of my service class such that when the test runs, data is not persisted.. I've looked into Mockito though don't see many examples or documentation regarding Mockito + TestNG in this context. Any help would be appreciated
First of all - if possible inject RestOperations in your service instead of RestTemplate. Then you will be able easily mock its behavior (note: RestTemplate implements RestOperations).
If using RestOperations is not possible - you can do something this:
RestTemplate myTemplate = Mockito.spy(new RestTemplate());
String expectedOutput = "hello mock";
String inputUrl = "https://stackoverflow.com/questions/53872148/unit-test-service-class-with-mocks-using-testng";
Mockito.doReturn(expectedOutput).when(myTemplate).getForObject(inputUrl, String.class);
String result = myTemplate.getForObject(inputUrl, String.class);
Assert.assertEquals(expectedOutput, result);
I've actually constructed a method using Mockito as follows... there might be a more elegant solution so I would be interested to see:
public class ServiceTest {
#BeforeMethod(groups="serviceTest")
public void beforeMethod() {
MockitoAnnotations.initMocks(this);
}
#Test(groups="serviceTest")
public void testGetService_returns200() {
when(serviceDao.getService(any(String.class), any(RestTemplate.class), any(HttpHeaders.class))).thenReturn(new ResponseEntity(new Object(), HttpStatus.OK));
ObjectRequest request = new ObjectRequest();
// set request values
ResponseEntity testResponse = apiService.getObject(request);
}
}

Spring boot testing of a rest client using #RestClientTest

I am using spring boot 1.5.8 and want to test my client:
#Component
public class RestClientBean implements RestClient {
private Map<String, RestTemplate> restTemplates = new HashMap<>();
#Autowired
public RestClientBean(RestTemplateBuilder builder, SomeConfig conf) {
restTemplates.put("first", builder.rootUri("first").build();
restTemplates.put("second", builder.rootUri("second").build();
}
}
with the following test:
#RunWith(SpringRunner.class)
#RestClientTest(RestClient.class)
public class RestClientTest {
#Autowired
private RestClient client;
#Autowired
private MockRestServiceServer server;
#TestConfiguration
static class SomeConfigFooBarBuzz {
#Bean
public SomeConfig provideConfig() {
return new SomeConfig(); // btw. not sure why this works,
// but this is the only way
// I got rid of the "unable to load
// SomeConfig auto-wire" or something like this :)
// Anyway not my main issue
// EDIT: just realized that the whole
// #TestConfiguration part can be avoided by
// adding SomeConfig.class to the classes in the
// #RestClientTest annotation
}
}
#Before
public void setUp() throws Exception {
server.expect(requestTo("/somePath")) // here an exception is thrown
// (main issue)
.andRespond(withSuccess("<valid json>", MediaType.APPLICATION_JSON));
}
}
The exception is very clear:
java.lang.IllegalStateException: Unable to use auto-configured
MockRestServiceServer since MockServerRestTemplateCustomizer has been bound to
more than one RestTemplate
But is it somehow possible to get this tested or is it not allowed to instantiate two different rest templates in one client class?
I have just the need to use the first rest template in some cases and the second one in some others.
After some days of investigation and communication with spring folks via GitHub I found a solution for me and not receiving an answer here means my solution might be valuable for someone:
#RunWith(SpringRunner.class)
#RestClientTest(RestClient.class)
public class RestClientTest {
#Autowired
private RestClient client;
private MockRestServiceServer firstServer;
private MockRestServiceServer secondServer;
private static MockServerRestTemplateCustomizer customizer;
#TestConfiguration
static class RestTemplateBuilderProvider {
#Bean
public RestTemplateBuilder provideBuilder() {
customizer = new MockServerRestTemplateCustomizer();
return new RestTemplateBuilder(customizer);
}
}
#Before
public void setUp() {
Map<RestTemplate, MockRestServiceServer> servers = customizer.getServers();
// iterate and assign the mock servers according to your own strategy logic
}
#Test
public void someTest() {
firstServer.expect(requestTo("/somePath"))
.andRespond(withSuccess("some json body"),
MediaType.APPLICATION_JSON));
// call client
// verify response
}
Basically specify a number of mock servers same as the number of rest templates you use in your client code, then specify a test configuration providing a rest builder with a customizer, so that your client code's rest templates will be built via this customized builder. Then use the customizer to get the mock servers bounded to the created rest templates and define expectations on them as you want.

Spring-Boot RestClientTest not correctly auto-configuring MockRestServiceServer due to unbound RestTemplate

EDIT: This question is specifically pertaining to the #RestClientTest annotation introduced in spring-boot 1.4.0 which is intended to replace the factory method.
Problem:
According to the documentation the #RestClientTest should correctly configure a MockRestServiceServer to use when testing a REST client. However when running a test I am getting an IllegalStateException saying the MockServerRestTemplateCustomizer has not been bound to a RestTemplate.
Its worth noting that I'm using Gson for deserialization and not Jackson, hence the exclude.
Does anyone know how to correctly use this new annotation? I haven't found any examples that require more configuration then when I have already.
Configuration:
#SpringBootConfiguration
#ComponentScan
#EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
public class ClientConfiguration {
...
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}
}
Client:
#Service
public class ComponentsClientImpl implements ComponentsClient {
private RestTemplate restTemplate;
#Autowired
public ComponentsClientImpl(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public ResponseDTO getComponentDetails(RequestDTO requestDTO) {
HttpEntity<RequestDTO> entity = new HttpEntity<>(requestDTO);
ResponseEntity<ResponseDTO> response =
restTemplate.postForEntity("/api", entity, ResponseDTO.class);
return response.getBody();
}
}
Test
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private MockRestServiceServer server;
#Test
public void getComponentDetailsWhenResultIsSuccessShouldReturnComponentDetails() throws Exception {
server.expect(requestTo("/api"))
.andRespond(withSuccess(getResponseJson(), APPLICATION_JSON));
ResponseDTO response = client.getComponentDetails(requestDto);
ResponseDTO expected = responseFromJson(getResponseJson());
assertThat(response, is(expectedResponse));
}
}
And the Exception:
java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate
Answer:
As per the answer below there is no need to declare a RestTemplateBuilder bean into the context as it is already provided by the spring-boot auto-configuration.
If the project is a spring-boot application (it has #SpringBootApplication annotation) this will work as intended. In the above case however the project was a client-library and thus had no main application.
In order to ensure the RestTemplateBuilder was injected correctly in the main application context (the bean having been removed) the component scan needs a CUSTOM filter (the one used by #SpringBootApplication)
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
})
The MockRestServiceServer instance should be constructed from the static factory, using a RestTemplate. See this article for a detailed description of the testing process.
In your example, you can do:
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private RestTemplate template;
private MockRestServiceServer server;
#Before
public void setUp() {
server= MockRestServiceServer.createServer(restTemplate);
}
/*Do your test*/
}
You have RestTemplateBuilder at two places. At ClientConfiguration class and at ComponentsClientImpl class. Spring boot 1.4.0 auto-configure a RestTemplateBuilder which can be used to create RestTemplate instances when needed. Remove below code from ClientConfiguration class and run your test.
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}

Categories

Resources