I have one service called PostService, which has instance variable called connectionManager Autowired.
Inside this connectionManager there is a one instance variable called restTemplate Autowired.
And in the configuration java file, object of RestTemplate is created with some logic.
When I write a test case around it, and create a MockRestServiceServer with an expected URL and post method, and in return expect a response with some body. and when I execute a test case I don't get mocked response from this mockedRestServiceServer.
As much, I can sense, this is because during test execution, a real object of rest template is created and mock server is not used to send mocked response.
can someone help me, how to overcome from this ?
class PostServie {
#Autowired
private ConnectionManager connectionManager;
public void postMessage(String msg) {
// some logic
}
}
#Component
class ConnectionManager {
#Autowired
private RestTemplate restTemplate;
public String getToken(){
ResponseEntity<String> response = this.restTemplate.postForEntity(url, request, String.class);
//returns response.body() in string format
}
}
#Configuration
class Configuration {
#Bean
public RestTemplate getRestTemplate(){
// some logic and returns object of RestTemplate
}
}
#RunWith(SpringRunner.class)
class PostServiceTest {
#Autowired
private PostMessageService postMessageService;
#Resource(name="authServerRestTemplet")
private RestTemplate authServerRestTemplet;
private MockRestServiceServer mockedAuthServerRestTemplet;
private final String requestToAuthServer ="grant_type=client_credentials&client_id=ceapiClientId";
#Before
public void setUp() {
mockedAuthServerRestTemplet =
MockRestServiceServer.createServer(authServerRestTemplet);
}
#Test
public void postServeiceSuccess () {
mockedAuthServerRestTemplet.expect(requestTo(ACCESS_TOKEN_URI)).andExpect(content().string(requestToAuthServer)).andExpect(method(HttpMethod.POST)).andRespond(withSuccess("{abc : 'abc'}", MediaType.APPLICATION_JSON));
postMessageService.postMessage(jsonMessage);
}
}
}
You're creating an instance of RestTemplate but not using it. What is MockRestServiceServer doing?
I can also see a wrong utilisation of #Resource and #Autowired, which you shouldn't specify in your test. Try use #MockBean.
Autowiring works so that the resource you create in your test is injected in the real class, not the other way round.
I'll just add some sample code here, which might help you achieve your goal.
#RunWith(SpringRunner.class)
#WebMvcTest
public abstract class AbstractControllerTest {
#Autowired
protected MockMvc mockMvc;
#MockBean
protected Service service;
#Before
public void setUp() {
Mockito.reset(service);
}
}
and then your test
public class YourTest extends AbstractControllerTest {
#Test
public void shouldDoSomething() throws Exception {
// given
List<someStuff> stuff = new ArrayList<>();
stuff.add(new Whatever("content", "John Smith");
// when - service is in the abstract controller!
when(service.getSomething()).thenReturn(stuff);
// then
mockMvc.perform(get("/posts/1/whatever").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].content", is("content")))
.andExpect(jsonPath("$[0].author", is("John Smith"))));
}
Related
The behavior that I am seeing is a NullPointerException in the second test when the mocked restTemplate called. That pointed to a problem in the resetting of the mock. What surprised me is the fix ( that made both tests pass).
Modifying the code from
#MockBean private RestTemplate restTemplate;
to
#MockBean(reset = MockReset.NONE) private RestTemplate restTemplate;
fixed the issue. A few questions here:
Why didn't the default #MockBean behavior of MockReset.RESET work?
Is there something wrong with how I have set up my test such that default MockReset.RESET was failing?
Is there something wrong with the test config class?
Hopefully, I've provided enough context to answer the question.
I've created a simplified example of what I'm seeing:
Test configuration:
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
#Primary
public ObjectNode getWeatherService(RestTemplate restTemplate) {
return new WeatherServiceImpl(restTemplate);
}
}
The test:
#SpringBootTest
#ActiveProfiles("test")
#AutoConfigureMockMvc
class SamTest {
#Autowired private MockMvc mockMvc;
#MockBean private RestTemplate restTemplate;
/*
Works:
#MockBean(reset = MockReset.NONE) private RestTemplate restTemplate;
Fails:
#MockBean(reset = MockReset.BEFORE) private RestTemplate restTemplate;
#MockBean(reset = MockReset.AFTER) private RestTemplate restTemplate;
*/
#Test
public void testOne() throws Exception {
Mockito.when(restTemplate.getForEntity("http://some.weather.api", ObjectNode.class))
.thenReturn(new ResponseEntity("{\"weather\" : \"rainy\"}", HttpStatus.OK));
// Makes call to standard #RestController with a #GetMapping
// Call to external API is contained in #Service class.
// Currently controller just passes through the json from the underlying service call.
this.mockMvc.perform(
get("/weather/check").
contentType(MediaType.APPLICATION_JSON_VALUE)).
andExpect(status().isOk());
}
#Test
public void testTwo() throws Exception {
Mockito.when(restTemplate.getForEntity("http://some.weather.api", ObjectNode.class))
.thenReturn(new ResponseEntity("{\"error\" : \"bandwidth\"}", HttpStatus.BANDWIDTH_LIMIT_EXCEEDED));
this.mockMvc.perform(
get("/weather/check").
contentType(MediaType.APPLICATION_JSON_VALUE)).
andExpect(status().is5xxServerError());
}
}
The service:
#Service
public class WeatherServiceImpl implements WeatherService {
private final RestTemplate restTemplate;
#Autowired
public WeatherServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public ObjectNode retrieve(URI uri) {
ResponseEntity<ObjectNode> response = restTemplate.getForEntity(uri, ObjectNode.class);
return response.getBody();
}
}
There is a misunderstanding about the #MockBean default behaviour:
Why didn't the default #MockBean behavior of MockReset.RESET work? Is
there something wrong with how I have set up my test such that default
MockReset.RESET was failing?
From the MockBean.reset method documentation:
The reset mode to apply to the mock bean. The default is
MockReset.AFTER meaning that mocks are automatically reset after each
test method is invoked.
So your MockBean will be reset and unregistered from the application context after your first testcase execution and then your second testcase will find it null, while it will not happen in case of #MockBean(reset = MockReset.NONE) as you have done.
I'm having an issue while testing Rest request.
However test is being intercepted by one of my interceptors.
In short I want to mock interceptors behaviour rather than calling actual implementation.
For simplicity purposes to visualise the issue here is a simply Get request
#Controller
public class HiController {
#ResponseBody
#GetMapping(value = "/hi", produces = {"application/json"})
public String hi() {
return "hi";
}
}
with simple interceptor and config file
#Component
public class LoggingInterceptor implements HandlerInterceptor {
#Override
public final boolean preHandle(HttpServletRequest request, final HttpServletResponse response, final Object handler) {
System.out.println("Logging token interceptor");
return true;
}
}
#Component
public class InterceptorAppConfig implements WebMvcConfigurer {
#Autowired
private LoggingInterceptor loggingTokenInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingTokenInterceptor);
}
}
the purpose of test was to call rest endpoint and to mock interceptor
#RunWith(SpringRunner.class)
#WebMvcTest(HiController.class)
public class HiControllerTest extends MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
RestTemplate restTemplate;
#Mock
private HiController controller;
private static final String AUTHENTICATE_USER = "/hi";
#Test
public void mandatoryFieldMissing() throws Exception {
this.mockMvc.perform(get(AUTHENTICATE_USER))
.andExpect(content().string(containsString("hi")))
.andExpect(status().isOk());
}
}
#RunWith(SpringRunner.class)
public abstract class MvcTest {
#MockBean
LoggingInterceptor loggingTokenInterceptor;
}
When I run this test ^ LoggingInterceptor is still being called.
How can I configure tests to have mock interceptors?
You are using #MockBean. From the documentation,
If a bean, compatible with the declared class exists in the context, it replaces it by the mock. If it is not the case, it adds the mock in the context as a bean.
The mocked bean does not have behavior. Realize that by mocking the bean, the onus is on you to define the behavior.
You can either disable mocking the bean(to allow for default behavior)
Mockito.doCallRealMethod().when(loggingTokenInterceptor).preHandle(Mockito.any(), Mockito.any(), Mockito.any());
Or you can define the behavior for the mocked bean yourself.
I'm consuming a remote api in my Spring MVC project using RestTemplate. One client is for authentication named LoginApiClient and I have to use this in all other rest clients.
This one my rest client that uses LoginApiClient and consumes QrCode Api.
#Service
public class QrKeyApiClient implements QrKeyApiClientBase {
private RestTemplate restTemplate;
#Autowired
private LoginApiClientBase loginApiClient;
public QrKeyApiClient(RestTemplateBuilder builder) {
restTemplate = builder.build();
restTemplate.setErrorHandler(new ErrorHandler());
}
//Other implementation details
}
And this is LoginApiClient
#Service
public class LoginApiClient implements LoginApiClientBase {
private RestTemplate restTemplate;
public LoginApiClient(RestTemplateBuilder builder) {
restTemplate = builder.build();
restTemplate.setErrorHandler(new ErrorHandler());
}
//Other implementation details
}
My unit test class for QrKeyApiClient is like below.
#RunWith(SpringRunner.class)
#RestClientTest({QrKeyApiClient.class})
#Category(IUnitTest.class)
public class QrKeyApiClientTest {
#Value("${getQrCodeUrl}")
private String getQrCodeUrl;
#Mock
LoginApiClient loginApiClient;
#Autowired
private MockRestServiceServer server;
#InjectMocks
private QrKeyApiClient client;
#Test
public void getQrCodeAsImage_makesTrueCallToApi() throws Exception {
ResponseEntity<String> responseEntity = mock(ResponseEntity.class);
HttpEntity requestEntity = new HttpEntity<>(qrCodeGenerateRequest,new HttpHeaders());
this.server
.expect(requestTo(this.getQrCodeUrl))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("successResult", MediaType.TEXT_PLAIN));
String qrImage = this.client.getQrCodeAsImage(qrCodeGenerateRequest);
server.verify();
assertThat(qrImage, is(notNullValue(String.class)));
}
}
I'm not sure this is the right way to do this but I want to mock my LoginApiClient in this test and inject it to QrKeyApiClient. But my test not passes with an error that says "Error creating bean with name 'qrKeyApiClient': Unsatisfied dependency expressed through field 'loginApiClient';"
So how I can test this client with mock of other clients that member of it.
It seems the problem is a mixed usage of Spring and Mockito annotations. Spring provides the #MockBean annotation to mock services in the application context.
Try replacing #Mock with #MockBean and #InjectMocks with #Autowired. The requestEntity and responseEntity are not needed then probably.
#RunWith(SpringRunner.class)
#RestClientTest({QrKeyApiClient.class})
#Category(IUnitTest.class)
public class QrKeyApiClientTest {
#Value("${getQrCodeUrl}")
private String getQrCodeUrl;
#MockBean
LoginApiClient loginApiClient;
#Autowired
private MockRestServiceServer server;
#Autowired
private QrKeyApiClient client;
#Test
public void getQrCodeAsImage_makesTrueCallToApi() throws Exception {
this.server
.expect(requestTo(this.getQrCodeUrl))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("successResult", MediaType.TEXT_PLAIN));
String qrImage = this.client.getQrCodeAsImage(qrCodeGenerateRequest);
server.verify();
assertThat(qrImage, is(notNullValue(String.class)));
}
}
I try to test a #RestController within a integration test suite using MockMvc.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class WebControllerIT {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}
The #RestController (WebController) calls an injected #Service (RestClientService) which uses RestTemplate to call another REST server. This leads to the following error when running the test.
org.springframework.web.client.ResourceAccessException: I/O error on
GET request for "http://test123.com/42/status": test123.com; nested
exception is java.net.UnknownHostException: test123.com
I used MockRestServiceServer for the integration test of the #Service itself but have no idea how to archieve this within the test of #RestController.
How can I simulate a correct REST call of the RestTemplate?
The #RestController class.
#RestController
public class WebController {
private final RestClientService service;
#Autowired
public WebController(RestClientService service) {this.service = service;}
#GetMapping("/status")
public String getStatus() {
// extract pid from database ...
int pid = 42;
return this.service.getStatus(42);
}
}
The #Serviceclass.
#Service
public class RestClientService {
private final RestTemplate restTemplate;
public RestClientService(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
}
Integration/Unit testing doesn't work that way.Objective of this kind of testing is to run through your code and make sure all the business requirement are met but not to hit other system or DB.Here in your case u shouldn't be hitting test123.com to get back data.What needs to done here is that you should mock that method.
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
So that control doesn't enter this method but return you back the mock data(Dummy data).
For example let say that there are 2 kind of status this method is returning and you need to do some business validation based on the string returned.In this case u need to write 2 integration test and make sure the mocking method returns 2 different value(Dummy value instead of hitting that end point)
Reason why we are writing unit testing/integration testing is to make sure your entire code is working as expected but not to hit other system from ur code.
If you want to only test your controller layer, you would do like this.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MockServletContext.class)
#WebAppConfiguration
public class WebControllerIT {
private MockMvc mockMvc;
private RestClientService service
#Mock
private RestTemplate restTemplate
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
service = new RestClientService(restTemplate);
WebController webController = new WebController(service);
mvc = MockMvcBuilders.standaloneSetup(webController).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
//Mock the behaviour of restTemplate.
doReturn("someString").when(restTemplate).getForObject(anyString(), anyString());
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}
#RunWith(MockitoJUnitRunner.class)
public class FeatureFlipperManagerTest {
#Autowired
RestTemplate restTemplate = new RestTemplate();
#Autowired
Service service = new Service();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
#Test
public void test() throws Exception {
mockServer.expect(requestTo(Mockito.anyString()))
.andRespond(withSuccess("{\"enabled\":true}", MediaType.APPLICATION_JSON));
boolean res = service.isEnabled("xxx");
mockServer.verify();
Assert.assertEquals(true, res);
}
}
I have MockRestServiceServer to mock restTemplete in a service. But it always fail. it shows the error as java.lang.AssertionError: Further request(s) expected
0 out of 1 were executed. Any one could let me know where I did not do it right.
The service itself will looks as this:
public class Service{
public boolean isEnabled(String xxx) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity("someurl",String.class);
if(...)return true;
return false;
}
}
First of all, your Service class creates a new instance of RestTemplate on every request. I cannot stress enough how bad practice it is. Create a bean of type RestTemplate and inject it into your Service bean (it is most likely already created - depending on the Spring MVC version you are using).
Once you have it, then both RestTemplates: one in your Service bean and one injected into FeatureFlipperManagerTest will be the same and testing with MockRestServiceServer will be possible.
EDIT - to be more explicit:
Modify your Service class to:
#Component
public class Service {
private RestTemplate restTemplate;
#Autowired
public Service(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public boolean isEnabled(String xxx) {
ResponseEntity<String> response = restTemplate.getForEntity("someurl",String.class);
if(...)return true;
return false;
}
}
and your test class to:
#RunWith(MockitoJUnitRunner.class)
public class FeatureFlipperManagerTest {
#Autowired
RestTemplate restTemplate;
#Autowired
Service service;
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
#Test
public void test() throws Exception {
mockServer.expect(requestTo(Mockito.anyString()))
.andRespond(withSuccess("{\"enabled\":true}", MediaType.APPLICATION_JSON));
boolean res = service.isEnabled("xxx");
mockServer.verify();
Assert.assertEquals(true, res);
}
}
If this fails with exception saying that there is no RestTemplate bean present then please paste info about version of Spring (Spring Boot?) you are using.
I think you mean you want to use RestTemplate which is provided by spring, so you should createServer after the spring autowired the RestTemplate. I think you can do it like this:
#RunWith(MockitoJUnitRunner.class)
public class FeatureFlipperManagerTest {
#Autowired
RestTemplate restTemplate;
Service service;
MockRestServiceServer mockServer;
#Before
public void init() {
service = new Service();
service.setRestTemplate(restTemplate);
// If you have autowired restTemplate in Service, you can just autowired the service
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void test() throws Exception {
mockServer.expect(requestTo(Mockito.anyString()))
.andRespond(withSuccess("{\"enabled\":true}", MediaType.APPLICATION_JSON));
boolean res = service.isEnabled("xxx");
mockServer.verify();
Assert.assertEquals(true, res);
}
}
This is not an answer to your question, but just in case anyone comes across this question in 2021… With Spring Boot Testing, you may want to take advantage of testing the REST slice only with #RestClientTest. This creates a RestTemplateBuilder bean only by default, if you want an auto-wired RestTemplate just add one bit of configuration as below. (The example is in Kotlin, using Java instead remains as an excercise to the reader.)
#AutoConfigureWebClient(registerRestTemplate = true)
#RestClientTest(Service::class)
class AdkClientTest #Autowired constructor(
private val mockRestServiceServer: MockRestServiceServer,
private val service: Service
) {
// …
}