I'm using Spring's ProxyExchange, and I want to change the default RestTemplate (in order to ignore certificates on dev env).
Is there a way to do that?
Using MVC (and not flux)
We altered RestTemplate using ProxyExchangeArgumentResolver class. below is code snippet
package com.vikrant.practice.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.gateway.mvc.config.ProxyExchangeArgumentResolver;
import org.springframework.cloud.gateway.mvc.config.ProxyProperties;
import org.springframework.cloud.gateway.mvc.config.ProxyResponseAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.util.Optional;
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.vikrant.practice"})
public class XyzConfig {
public RestTemplate restTemplate() {
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(30000).setConnectTimeout(30000).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(100);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
HttpClient httpClient = HttpClientBuilder
.create().setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingHttpClientConnectionManager)
.disableCookieManagement().build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectionRequestTimeout(1000);
factory.setReadTimeout(30000);
factory.setConnectTimeout(30000);
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
return restTemplate;
}
#Bean
public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(
Optional<RestTemplateBuilder> optional, ProxyProperties proxy) {
RestTemplate template = restTemplate();
template.getMessageConverters().add(new ByteArrayHttpMessageConverter() {
#Override
public boolean supports(Class<?> clazz) {
return true;
}
});
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(template);
resolver.setHeaders(proxy.convertHeaders());
resolver.setSensitive(proxy.getSensitive());
return resolver;
}
}
This was done for SpringBoot application. In case you want to use it in web application then create a class extending SpringBootServletInitializer and implementing WebApplicationInitializer and in that under configure method wire this class.
So basically I built a RestTemplateCustomizer and added a bean to customize the default RestTemplate
Related
I'm using spring-boot 2.7.4 and spring-cloud-dependencies 2021.0.4.
I haven't found any solution in spring documentation for add trustedTypes in BatchMessagingMessageConverter. I'm using kafka for read messages in batch-mode. If I insert a custom header (my own class) when the consumer read the header return a DefaultKafkaHeaderMapper$NonTrustedHeaderType and not my class.
I have in my configuration this key to activate batch mode:
spring.cloud.stream.bindings.nameBind-in-0.consumer.batch-mode=true
I tried in debug to add to headerMapper in BatchMessagingMessageConverter the package of my class and all works fine. There is a way to specify my package in configuration?
I followed the documentation https://docs.spring.io/spring-cloud-stream/docs/3.2.5/reference/html/spring-cloud-stream-binder-kafka.html#kafka-binder-properties, I created a bean like this:
#Bean("kafkaHeaderMapperCustom")
KafkaHeaderMapper getKafkaHeaderMapperCustom() {
var defKHM = new DefaultKafkaHeaderMapper();
defKHM.addTrustedPackages("*");
return defKHM;
}
Specified to key spring.cloud.stream.kafka.binder.headerMapperBeanName in configuration but doesn't work, I suppose that configuration is valid for not batch context?
I tried also these properties:
spring.kafka.consumer.properties.spring.json.trusted.packages
spring.json.trusted.packages
EDIT - Add example:
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.support.DefaultKafkaHeaderMapper;
import org.springframework.kafka.support.KafkaHeaderMapper;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
#SpringBootApplication
public class Application {
public static final String HEADER_KEY = "CUSTOM_HEADER_KEY";
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ApplicationRunner runner(StreamBridge streamBridge) {
return args -> {
var headers = new MessageHeaders(Map.of(HEADER_KEY, new CustomHeaderClass("field1Value", LocalDate.now())));
headers.get(KafkaHeaders.BATCH_CONVERTED_HEADERS);
var message = MessageBuilder.createMessage(new ExampleBrokenHeaderEntity("randomValue", "randomName"), headers);
streamBridge.send("stackoverflow-example", message);
};
}
#Bean
public Consumer<Message<List<ExampleBrokenHeaderEntity>>> readFromKafkaBatchMode() {
return messages -> {
var brokenHeader = ((ArrayList<Map<String, Object>>) messages.getHeaders().get(KafkaHeaders.BATCH_CONVERTED_HEADERS)).get(0).get(HEADER_KEY);
System.out.println("BATCH - Class header: " + (brokenHeader != null ? brokenHeader.getClass() : null));
};
}
#Bean
public Consumer<Message<ExampleBrokenHeaderEntity>> readFromKafkaNoBatchMode() {
return messages -> {
var brokenHeader = messages.getHeaders().get(HEADER_KEY);
System.out.println("NO_BATCH - Class header: " + (brokenHeader != null ? brokenHeader.getClass() : null));
};
}
#Bean("kafkaHeaderMapperCustom")
public KafkaHeaderMapper getKafkaHeaderMapperBatchMode() {
var kafkaHeaderMapperCustom = new DefaultKafkaHeaderMapper();
kafkaHeaderMapperCustom.addTrustedPackages("*");
return kafkaHeaderMapperCustom;
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CustomHeaderClass {
private String field1;
private LocalDate field2;
}
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
public final class ExampleBrokenHeaderEntity {
private String type;
private String name;
}
spring.cloud.stream.kafka.binder.brokers=x.x.x.x:xxxx
spring.cloud.function.definition=readFromKafkaNoBatchMode;readFromKafkaBatchMode
spring.cloud.stream.bindings.readFromKafkaBatchMode-in-0.destination=stackoverflow-example
spring.cloud.stream.bindings.readFromKafkaBatchMode-in-0.group=readFromKafkaBatchMode
spring.cloud.stream.bindings.readFromKafkaBatchMode-in-0.consumer.batch-mode=true
spring.cloud.stream.bindings.readFromKafkaNoBatchMode-in-0.destination=stackoverflow-example
spring.cloud.stream.bindings.readFromKafkaNoBatchMode-in-0.group=readFromKafkaNoBatchMode
spring.cloud.stream.kafka.binder.headerMapperBeanName=kafkaHeaderMapperCustom
The output of example is:
NO_BATCH - Class header: class com.example.kafka.header.types.CustomHeaderClass
BATCH - Class header: class org.springframework.kafka.support.DefaultKafkaHeaderMapper$NonTrustedHeaderType
It's a bug; the binder only sets the custom header mapper on a record converter:
private MessageConverter getMessageConverter(
final ExtendedConsumerProperties<KafkaConsumerProperties> extendedConsumerProperties) {
MessageConverter messageConverter = BindingUtils.getConsumerMessageConverter(getApplicationContext(),
extendedConsumerProperties, this.configurationProperties);
if (messageConverter instanceof MessagingMessageConverter) {
((MessagingMessageConverter) messageConverter).setHeaderMapper(getHeaderMapper(extendedConsumerProperties));
}
return messageConverter;
}
There should be similar code for when the converter is a BatchMessagingMessageConverter.
The work around is to define a custom message converter for the batch consumer:
#Bean("batchConverter")
BatchMessageConverter batchConverter(KafkaHeaderMapper kafkaHeaderMapperCustom) {
BatchMessagingMessageConverter batchConv = new BatchMessagingMessageConverter();
batchConv.setHeaderMapper(kafkaHeaderMapperCustom);
return batchConv;
}
spring.cloud.stream.kafka.bindings.readFromKafkaBatchMode-in-0.consumer.converter-bean-name=batchConverter
NO_BATCH - Class header: class com.example.demo.So74294156Application$CustomHeaderClass
BATCH - Class header: class com.example.demo.So74294156Application$CustomHeaderClass
Please open an issue against Spring Cloud Stream, referencing this question/answer.
I want to test my spring boot application using TestRestemplate, however I am unable to test get url, what I am trying to do here is I am injecting some predefine objects setting into list and mocking find all method.
below is my test code looks like.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PersonControllerTests {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private PersonRepository mockRepository;
#Before
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
}
#Test
public void getPersonsGoodReq() throws Exception {
ResponseEntity<Person[]> response = restTemplate
.withBasicAuth("admin", "password")
.getForEntity("/persons/all", Person[].class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(response.getBody().length, 2);
}
}
I am expecting answer to be 2, but when I am seeing body response it is empty array.
what might have gone wrong I am unable to get
As you're using JUnit Jupiter, you have to use #BeforeEach. #Before is from JUnit 4 and hence wasn't invoked as part of the test's lifecycle:
#BeforeEach
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
}
I have a bit of trouble with this.
I'm trying to test the web layer of my Spring boot app (with JUnit5).
I'm using the #WebMvcTest(NoteController::class) to allow me to autowire MockMvc in order to mock requests.
But I get the below error :
kotlin.UninitializedPropertyAccessException: lateinit property mvc has not been initialized
NoteControllerTest
import org.hamcrest.Matchers.`is`
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
#ExtendWith(SpringExtension::class)
#WebMvcTest(NoteController::class)
class NoteControllerTest {
#Autowired
private lateinit var mvc: MockMvc
#Test
fun should_create_a_note() {
mvc.perform(
post("/notes"))
.andExpect(status().isCreated)
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.content", `is`("my content")))
}
}
NoteController
import fr.$$.$$.api.CreateNote
import fr.$$.$$.api.FetchNote
import fr.$$.$$.resources.Note
import fr.$$.$$.resources.toResource
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import java.net.URI
#RestController("/notes")
class NoteController(val createNote: CreateNote,
val fetchNote: FetchNote) {
#GetMapping
fun getAllNotes(): ResponseEntity<List<Note>> {
return ResponseEntity(fetchNote.all().toResource(), HttpStatus.OK)
}
#PostMapping
fun createNote(): ResponseEntity<Note> {
val note = createNote.with("my content").toResource()
return ResponseEntity.created(URI("")).body(note)
}
}
SmartNotesApplicationTest
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.test.context.junit.jupiter.SpringExtension
#ExtendWith(SpringExtension::class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
class SmartNotesApplicationTest {
#Test
fun contextLoad() {
}
}
Thanks in advance.
I inject the WebApplicationContext and then build a new MockMvc for each test from that.
#SpringBootTest
class SomeTest {
#Autowired
lateinit var webApplicationContext: WebApplicationContext
lateinit var mockMvc: MockMvc
#BeforeEach
fun beforeEach() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
}
// Tests go here!
}
thanks for the answers, I put the working answer in Java with Spring Boot 2.2.6:
AuthorController.class
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping(path = "/authors")
public class AuthorController {
#GetMapping("/health")
public boolean healthcheck() {
return true;
}
}
AuthorControllerIT.class
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#SpringBootTest
#AutoConfigureMockMvc
class AuthorControllerIT {
#Autowired
private MockMvc mockMvc;
#Test
public void test_web_layer() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/authors/health"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("true"));
}
}
Note :
You can use #WebMvcTest(AuthorController.class) instead of #SpringBootTest + AutoConfigureMockMvc
With this annotation, it will only load the web layer.
If you have dependencies in your controller (services, repositories...) you must use :
#MockBean
private MyService service;
I have a controller protected with HTTP Basic authentication.
I setup the app to use session cookies and it works.
However when I test the controller using MockMvc, a successful authentication does not give me any cookie.
Web configuration:
package world.pyb.spring.cookiesdemo;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("argentina").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.httpBasic()
.and().authorizeRequests().antMatchers(HttpMethod.GET, "/hello").authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
//#formatter:on
}
}
Simple controller:
package world.pyb.spring.cookiesdemo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloController {
#RequestMapping("/hello")
public String index() {
return "Greetings from Spring Boot!";
}
}
Simple controller test that doesn't give me the session cookie:
package world.pyb.spring.cookiesdemo;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class HelloControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/hello")
.with(SecurityMockMvcRequestPostProcessors.httpBasic("admin", "argentina"))
)
// prints "Cookies = []"
.andDo(MockMvcResultHandlers.print())
.andExpect(cookie().exists("JSESSIONID"))
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
Related questions:
Why does Spring MockMvc result not contain a cookie?
Unit Testing /login in Spring MVC using MockMvc
Spring MVC testing (security Integration test), JSESSIONID is not present
Some answers suggest not to use MockMvc but I'd like to keep using it if possible.
As shown in the question's code, MockMvcResultMatcher includes support for cookies.
This will work fine, as long as the controller under test itself delivers the cookie. The problem here is that the cookie is delivered by Spring Security, which is a wrapper around your controller. MockMvc is testing your controller directly, and not testing your controller running in its real HTTP server, as would be required to test the security-layer cookies.
That's why TestRestTemplate, which invokes your controller in its full server context, delivers a more thorough test environment.
Notice however that as of Spring 5, the newer approach to running-server API testing is based on WebTestClient.
I am trying to create a JUnit test case for spring mvc rest controller and service that is accessed by controller.
I am using Mockito for doing the above. I'm am able to successfully invoke the mock injected controller from the test case and also when I debug I see that the mocked userService is available in the mocked contoller, but the method within the service object is not getting invoked. While debugging I observe that it just steps over the service method call.
I do not see any kind of exception too.
I am using
Maven 3
Spring 4.1.1 Release version
Junit 4.11
Java version 1.6
I have pasted my code below:
1. Test Class
package controller;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/spring/application-config.xml")
public class UserControllerTest {
private MockMvc mockMvc;
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testRegisterUser()throws Exception{
User user = new User();
user.setCity("City");
user.setTown("Town");
user.setFirstname("Name");
user.setLastname("LastName");
user.setPassword("abc#123");
user.setUsername("abc#gmail.com");
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(object);
}
}
2. Controller under Test
package com.myPackage.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RestController
#RequestMapping("/service/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
#Autowired
private IUserService userService;
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/register/", method = RequestMethod.POST,headers="Accept=application/json")
public String registerUser(#RequestBody User user) {
logger.info("register user:"+user.toString());
String userDetails = userService.registerUser(user);
logger.info("user registered:"+userDetails);
return userDetails;
}
}
3. Service to be invoked within controller in test
The service method registerUser is to be invoked from controller above. I do not see any logs getting printed on the console when we run the test case. Also when debugging I see that userSerivice instance of type like this - IUserService$$EnhancerByMockitoWithCGLIB$$c00081eb is created but when I dig deep all to see the list of methods under mockhandlers registered methods, I only see 'toString' method in the list of invocations. Not sure if this gives some indication on why this method 'registerUser' in the service class below is not getting invoked during test case.
package com.myPackage.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import com.myPackage.dao.IUserDao;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
#Service("userService")
public class UserService implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
#Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
#Override
public String registerUser(User user) {
// TODO Auto-generated method stub
logger.info("New User for registration:",user);
if(user!=null && user.getUsername()!=null){
User alreadyExist = userDao.getUserByLogin(user.getUsername());
if(alreadyExist!=null){
return "userAlreadyExists";
}
}
logger.info("New User for registration complete:",user.getUser_id());
return null;
}
}
4. Interface implemented by UserService class in point 3 above
IUserService. The mocked userService in test class above is of type IUserService.
package com.myPackage.service;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
public interface IUserService {
public String registerUser(User user);
}
Your method (read code) within userService will never actually be invoked from your controller because, it has been mocked out by mockito.
You have defined this yourself by doing:
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
MockitoAnnotations.initMocks(this);
If you want to assert that the method has been called you can using,
verify(userService, times(1)).registerUser(any(User.class));
#Jonas: Thanks for your insight. If i don't mock userService then at the time of unit test, userService is found null under userController method matching registerUser. And so after reading somewhere i mocked userService. Also after doing the changes as suggested by you, the mocked service method still does not get invoked - Below are my changes -
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
If you want to test the actual service method, then autowire it:
#Autowired
private IUserService userService;
If you want to use a mock like you're doing now, you need to stub the method:
when(userService.registerUser(any(User.class))).thenReturn("expected string");