Mock object is not getting injected in Service Class when Cucumber is used with Mockito - spring

We are calling a third party service which I would like to mock and not call it. For Some reason, the mock RestTemplate doesn't get injected and the class has real "RestTemplate" object.
My cucumber class look like this
#RunWith(Cucumber.class)
#CucumberOptions(plugin = { "pretty", "html:build/cucumber",
"junit:build/cucumber/junit-report.xml" },
features = "src/test/resources/feature",
tags = { "#FunctionalTest","#In-Progress", "~#TO-DO" },
glue= "com.arrow.myarrow.service.order.bdd.stepDef")
public class CucumberTest {
}
and the StepDefinition looks like this
#ContextConfiguration(loader = SpringBootContextLoader.class, classes =
OrderServiceBoot.class)
#WebAppConfiguration
#SpringBootTest
public class BaseStepDefinition {
#Autowired
WebApplicationContext context;
MockMvc mockMvc;
#Rule public MockitoRule rule = MockitoJUnit.rule();
RestTemplate restTemplate = mock(RestTemplate.class);
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
//Telling rest template what to do
when(restTemplate.exchange(Mockito.anyString(), Mockito.
<HttpMethod>any(), Mockito.<HttpEntity<?>>any(), Mockito.
<Class<UserProfile>>any()))
.thenReturn(new ResponseEntity<>(userProfile,
HttpStatus.OK));
}
This is my service class looks like
#Autowired
RestTemplate restTemplate;
public UserProfile getUserProfile(OAuth2Authentication auth){
ResponseEntity<UserProfile> response
=restTemplate.exchange("http://localhost:8084/api/v1.0/user/profile", HttpMethod.GET,new HttpEntity<>(new HttpHeaders()),UserProfile.class);
return response.getBody();
}
In the service class, the RestTemplate restTemplate is not mocked, it contains the real object so it is trying to call the real service which is not intended.
Does anyone knows why Mocking isn't working here?

The way it worked for me is by creating a class in TestFolder and then defining a new bean for resttemplate which generates the MockRestTemplate instance.
#Configuration
#Profile("local")
public class CucumberMockConfig {
#Bean
#Primary
public RestTemplate getRestRemplate() {
return mock(RestTemplate.class);
}
}
In test class use (Dont use #Mock or Mock(restTemplate), as you don't want a new object)
#Autowired
RestTemplate restTemplate
#Before
public void setup() throws JsonProcessingException {
UserProfile userProfile = new UserProfile();
userProfile.setCompany("myCompany");
when(restTemplate.exchange(Mockito.endsWith("/profile"),
Mockito.<HttpMethod>eq(HttpMethod.GET),
Mockito.<HttpEntity<?>>any(),
Mockito.eq(UserProfile.class)))
.thenReturn(ResponseEntity.ok().body(userProfile));
}
and in service/config class use
#Autowired
RestTemplate restTemplate

Related

Spring-Boot 2.3.0.RELEASE Unable to autowire RestTemplate for JUnit 5 test

I have configured the necessary Beans in #Configuration class but have not been able to get the RestTemplate injected into my test class for testing.
#Configuration
public class AppConfig {
#Bean
public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufHttpMessageConverter();
}
#Bean
public RestTemplate restTemplate(ProtobufHttpMessageConverter converter) {
RestTemplate http2Template = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
List<HttpMessageConverter<?>> converters = http2Template.getMessageConverters();
converters.add(converter);
http2Template.setMessageConverters(converters);
return http2Template;
}
}
Test class:
#ExtendWith(SpringExtension.class)
#AutoConfigureWebClient(registerRestTemplate = true)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {RestTemplate.class, ProtobufHttpMessageConverter.class})
#ActiveProfiles("dev")
public class GRPCRestApiTest {
#Autowired
private RestTemplate restTemplate;
#Test
public void GetOneCourseUsingRestTemplate() throws IOException {
assertNotNull(restTemplate, "autowired restTemplate is NULL!");
ResponseEntity<Course> course = restTemplate.getForEntity(COURSE_URL, Course.class);
assertResponse(course.toString());
HttpHeaders headers = course.getHeaders();
}
}
Any advice and insight is appreciated
The classes attribute of the annotation #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {RestTemplate.class, ProtobufHttpMessageConverter.class}) takes component classes to load the application context. You should not put in here anything except your main Spring Boot class or leave it empty.
Furthermore #AutoConfigureWebClient(registerRestTemplate = true) as you want to use the bean you configure inside your application (at least that's what I understood from your question).
So your test setup should look like the following:
// #ExtendWith(SpringExtension.class) can be omitted as it is already part of #SpringBootTest
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ActiveProfiles("dev")
public class GRPCRestApiTest {
#Autowired
private RestTemplate restTemplate;
#Test
public void GetOneCourseUsingRestTemplate() throws IOException {
assertNotNull(restTemplate, "autowired restTemplate is NULL!");
ResponseEntity<Course> course = restTemplate.getForEntity(COURSE_URL, Course.class);
assertResponse(course.toString());
HttpHeaders headers = course.getHeaders();
}
}
This should now start your whole Spring Boot context in dev profile and you should have access to all your beans you define inside your production code like AppConfig.

I want to mock a server before bean creation in spring boot integration test

I am writing Integration tests in spring-boot.
One of my beans is using UrlResource("http://localhost:8081/test") for its creation.
I want to create a mock server, which will serve the above url with a mock response.
But I want this mock server to be created before any bean is initialized, as the mock server should be available to serve requests before the bean is initialized.
I have tried using the MockRestServiceServer in the #TestConfiguration
Following is the pseudo code which is failing:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestApiGatewayApplicationTests {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void contextLoads() {
}
#TestConfiguration
class TestConfig {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
String mockKeyResponse = "{\"a\":\"abcd\"}";
mockServer.expect(requestTo("http://localhost:8081/test"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(mockKeyResponse, MediaType.TEXT_PLAIN));
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
when(builder.build()).thenReturn(restTemplate);
return builder;
}
}
}
Following is the sample code for creation of bean which is to be tested.
#Configuration
public class BeanConfig {
#Bean
public SampleBean sampleBean(){
Resource resource = new UrlResource("");
// Some operation using resource and create the sampleBean bean
return sampleBean;
}
}
Using the above approach I am getting
" java.net.ConnectException: Connection refused (Connection refused)"
error as it is not able to access the http://localhost:8081/test endpoint.
Use #InjectMocks.
Reference : documentation and Explaination with example.
I have solved this issue by creating a MockServiceServer in the testConfiguration.
Sample code is as follows.
#TestConfiguration
static class TestConfig {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
String mockKeyResponse = "{\"a\":\"abcd\"}";
mockServer.expect(requestTo("http://localhost:8081/test"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(mockKeyResponse, MediaType.TEXT_PLAIN));
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
when(builder.build()).thenReturn(restTemplate);
return builder;
}
}
Then in the class BeanConfig where I needed to use this I have autowired using constructor injection, so that RestTemplate will be created before bean of BeanConfig class is created.
Following is how I did it.
#Configuration
public class BeanConfig {
private RestTemplate restTemplate;
public BeanConfig(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Bean
public SampleBean sampleBean(){
Resource resource = new UrlResource("");
// Some operation using resource and create the sampleBean bean
return sampleBean;
}
}

how to create RestController in test directory for SpringBoot Application

Im currently writing integration test for SpringBoot Application .
It's functionality is to receive/send request from outside and forward/receive them to another application(APP_2). So there are two systems which needs to be mocked outside System and APP_2 .
HomeController
#Controller
public class HomeController {
#Autowired
ForwardController forwardController;
#RequestMapping("/")
public #ResponseBody
String greeting() {
return forwardController.processGET().toString();
}
}
ForwardController
#Service
public class ForwardController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment environment;
private ResponseEntity sendRequest(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) {
return restTemplate.exchange( url, method, requestEntity, responseType,uriVariables);
}
public ResponseEntity processGET()
{
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> entity = new HttpEntity<>(headers);
String app_2_url = environment.getProperty(Constants.APP_2_URL);
ResponseEntity<String> response = sendRequest(app_2_url,HttpMethod.GET,entity,String.class);
return response;
}
}
APP_2_CONTROLLER
#Controller
public class App_2_Controller {
#RequestMapping("/app2Stub")
public #ResponseBody
String greeting() {
return "Hello End of world";
}
}
Test Class which simulates the external request behavior to the system:
HTTP_request_Test
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Application.class)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private Environment environment;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello End of world");
}
}
Here in this test class I'm overriding the properties by having two property file. So when we run test the request would be sent to App_2_Controller ( Mock in my project ) rather than the real App .
QUESTION :
Is there any way to have the APP_2_CONTROLLER inside the test folder ? This is because I don't want to expose the unwanted test endpoint in my Actual application .
Here in the above project , Im changing the URL with properties. Is there a better way to put a controller for the same URL. For simplicity sake lets assume, app_2 url is app.com:9000/serve
Spring already comes with a MockRestServiceServer, that makes this a lot easier so that you don't have to create your own dummy controllers (App_2_Controller). So in your case, you can remove that controller, and write a test like this for ForwardController:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class ForwardControllerTest {
#Autowired
private RestTemplate restTemplate;
#Autowired
private ForwardController forwardController; // Your service
private MockRestServiceServer server;
#Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
#Test
public void processGet_returnsResponseFromAPI() {
server.expect(once(), requestTo("http://app.com:9000/serve"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("Hello End of world", MediaType.TEXT_PLAIN));
assertThat(forwardController.processGET().getBody()).isEqualTo("Hello End of world"));
}
}
Additionally, you can create a separate test for your actual controller (ForwardController is just a service), mock ForwardController and use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest
public class HomeControllerTest {
#Autowired
private HomeController homeController;
#Autowired
private MockMvc mockMvc;
#MockBean
private ForwardController forwardController;
#Test
public void greeting_usesForwardController() {
when(forwardController.expectGET()).thenReturn("Hello End of world");
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello End of world")));
}
}
In this case, you'll end up with two tests:
One test to verify that RestTemplate is used to capture the proper response from your external REST API.
Another test to verify that HomeController just forwards whatever ForwardController responds.

Is it possible to use MockMvc and mock only the RestTemplate used by my service?

I'm trying to build one integration test using MockMvc and I want to mock only the RestTemplate used by MyService.java. If I uncomment the code on MyIT.java, this test will fail because the RestTemplate used by MockMvc will be mocked as well.
MyRest.java
#RestController
public class MyRest {
#Autowired
private MyService myService;
#RequestMapping(value = "go", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<String> go() throws IOException {
myService.go();
return new ResponseEntity<>("", HttpStatus.OK);
}
}
MyService.java
#Service
public class MyService {
#Autowired
private RestTemplate restTemplate;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public void go() {
restTemplate.getForObject("http://md5.jsontest.com/?text=a", String.class);
}
}
MyIT.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
//#RestClientTest(MyService.class)
public class MyIT {
#Autowired
private MockMvc mockMvc;
// #Autowired
// private MockRestServiceServer mockRestServiceServer;
#Test
public void shouldGo() throws Exception {
// mockRestServiceServer.expect(requestTo("http://md5.jsontest.com/?text=a"))
// .andRespond(withSuccess());
mockMvc.perform(get("/go")).andExpect(status().isOk());
}
}
First, you should #Autowired your RestTemplate bean to your test
class.
Then create the MockRestServiceServer with the restTemplate, instead of
autowiring it.
Perhaps try this one:
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockRestServiceServer;
#Before
public void setup() {
mockRestServiceServer= MockRestServiceServer.createServer(restTemplate);
}

How to mock a class that been annotated with Primary

I have :
an interface : EntityService
a first implementation : EntityServiceImpl - This class is annotated with #Primary
an other one : EntityServiceClientImpl
and a controller that has this field #Autowired EntityService
I would like to do a test on this controller and for this test to be unitary I mock EntityService.
So of course this code does not work because Spring detects two beans annotated with Primary :
#Configuration
class EntityControllerTestConfig {
#Bean
#Primary
EntityService entityService() {
return mock(EntityService.class);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#WebAppConfiguration
#ContextConfiguration(classes = EntityControllerTestConfig.class)
public class EntityControllerTest {
#Autowired
private EntityService entityService;
...
#SpringBootApplication(scanBasePackages= "com.company.app")
#EntityScan (basePackages = {"com.company.app" }, basePackageClasses = {Jsr310JpaConverters.class })
#EnableJpaRepositories("com.company.app")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
I tried to find an other way to mock and to exclude EntityServiceClient on test configuration but i was not able to mock. (cf : exclude #Component from #ComponentScan )
I finaly found that solution : a spring context (with controller, controllerAdvice and mock of service) and not a spring boot context
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class EntityControllerTest {
#Configuration
public static class EntityControllerTestConfig {
#Bean
public EntityService entityService() {
return mock(EntityService.class);
}
#Bean
public EntityController entityController() {
return new EntityController(entityService());
}
}
#Autowired
private EntityService entityService;
#Autowired
private EntityController entityController;
private MockMvc mockMvc;
#Before
public void setup() throws DomaineException {
this.mockMvc = MockMvcBuilders
.standaloneSetup(entityController)
.setControllerAdvice(new myControllerAdvice())
.build();
NB : Since Spring 4.2 you can set your ControllerAdvice like that.
You can approach it slightly differently and combine #WebMvcTest with #MockBean annotation to test just the controller with it's own minimal context.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = EntityController.class)
public class EntityControllerTest {
#MockBean
private EntityService entityService;
#Autowired
private MockMvc mvc;
In this example EntityService will be mocked while MockMvc can be used to assert the request mappings in controller.

Resources