This is the test class:
#MockBean
private UserRepository userRepository;
#Before
public void beforeClass() {
String mobile;
when(this.userRepository.findByMobile(Mockito.anyString())).thenAnswer(new Answer<User>() {
#Override
public User answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return MockData.getUserByMobile((String) args[0]);
}
});
}
#Test
#WithUserDetails(value = MockData.CUSTOMER_USERNAME, userDetailsServiceBeanName = "customizedUserDetailsService")
public void testAdd() throws Exception {}
And this is the userDetails implementation:
#Autowired
private UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) {
User user = (User) userRepository.findByMobile(username); // user is always null
What I expect is when userRepository.findByMobile is called, it should call the getUserByMobile method defined in #Before. But obviously the Mockito config does not work OR userRepository fail to mock. What's wrong and how to solve it?
UserRepository is used in userDetails implementation, and it needs to be injected into userDetails as described in this. However because XXRepository is in interface, so #InjectedMock cannot be used. Then classes become:
Test class:
#MockBean
private UserService userService;
#InjectMocks
private CustomizedUserDetailsService customizedUserDetailsService;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
when(this.userService.findByMobile(Mockito.anyString())).thenAnswer(new Answer<User>() {
#Override
public User answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return MockData.getUserByMobile((String) args[0]);
}
});
}
#Test
#WithUserDetails(value = MockData.CUSTOMER_USERNAME, userDetailsServiceBeanName = "customizedUserDetailsService") {}
And userDetails:
#Autowired
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) {
User user = (User) userService.findByMobile(username);
I can see that the userService in userDetails is the same userService mocked in test class, however #Before method is called after the #WithUserDetails userDetails. So finally in order to achieve loading MockData user been, I think I have to create another userDetails just for UT. EDIT 2: Actually, I have tried it without #InjectMocks and using userService (originally I was using userRepository), it works too.
Related
Hi I'm trying to write some tests with Mockito and PowerMockito (I need to mock private methods) for a rest service written with SpringMVC and I'm facing the following issue
This is the semplified version of the controller
#Controller
#RequestMapping(value = "/test")
public class SimpleController {
#Autowired
private HttpServletRequest httpRequest;
#RequestMapping(value = "/simpleservice", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
#ResponseBody
public SimpleServiceResponse simpleService(#RequestBody SimpleServiceRequest simpleServiceRequest, HttpServletRequest httpServletRequest) {
SimpleServiceResponse simpleServiceResponse=new SimpleServiceResponse(simpleServiceRequest.getValue());
httpRequest.getHeader("Header");
return simpleServiceResponse;
}
}
and this is the correspoding test class
#WebAppConfiguration
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WebApplicationContext.xml","classpath:SimpleApplicationContext.xml"})
#PrepareForTest(WebController.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class TestSimpleControllerMockito {
private Logger logger = LoggerFactory.getLogger(TestSimpleControllerMockito.class.getName());
private ObjectMapper objectMapper= new ObjectMapper();
#InjectMocks
private SimpleController controller;
#Test
public void testSimpleService() throws Exception {
MockitoAnnotations.initMocks(this);
SimpleService mockedSimple = mock(SimpleService.class);
when(mockedSimple.doSimpleService(any(SimpleServiceRequest.class))).thenReturn(new SimpleServiceResponse("MockMock"));
SimpleController mockedController=PowerMockito.spy(controller);
SimpleServiceRequest simpleServiceRequest= new SimpleServiceRequest("ciao");
String requestAsStr=objectMapper.writeValueAsString(simpleServiceRequest);
MockMvc mMockMvc=MockMvcBuilders.standaloneSetup(mockedController).build();
MvcResult result= mMockMvc.perform(post("/test/simpleservice").content(requestAsStr).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
SimpleServiceResponse simpleServiceResponse=objectMapper.readValue(content,SimpleServiceResponse.class);
Assert.assertEquals("MockMockMockedSessionManager",simpleServiceResponse.getValue());
}
}
When running the test case I got a NullPointerEception on httpRequest.getHeader("Header");
My guess is that using the #InjectMocks annotation and then using
SimpleController mockedController=PowerMockito.spy(controller);
is the cause of the NullPointerException. I don't know how to preserve the #Autowire annotation processing on the controller Object. I already found a workaround, but it requires to write some redundant code.
Is there a way to make the #autowired annotation work?
Thanks a lot.
P.S.
the SimpleServiceXXX classes are like this one:
public class SimpleServiceResponse {
private String value;
public SimpleServiceResponse() {
}
public SimpleServiceResponse(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I have UserBean in my Spring-MVC project to store user.
#Component
#Scope(value="session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserBeanImpl implements UserBean {
private User user;
#Override
public User getUser() {
return user;
}
#Override
public UserBean setUser(User user) {
this.user = user;
return this;
}
}
Can I autowire it into service layer? Should I autowire it only into controllers?
First way (autowire into servise):
#Service
public class MyServiceImpl implements MyService {
#Autowired
UserBean userBean;
#Override
public void doSomething(int id) {
dao.doSomething(id,userBean.getUser());
}
Second way (autowire into cotroller):
#Service
public class MyServiceImpl implements MyService {
#Override
public void doSomething(int id, User user) {
dao.doSomething(id, user);
}
}
#Controller
public class MyController {
#Autowired
UserBean userBean;
#RequestMapping(value = {"/"})
public void do(#RequestParam("id") int id) {
myService.doSomething(id, userBean.getUser());
}
}
What is better in terms of Spring MVC-Service-DAO architecture?
#Service
public class MyServiceImpl implements MyService {
#Autowired
UserBean userBean;
#Override
public void doSomething(int id) {
dao.doSomething(id,userBean.getUser());
}
This is the best way (Service), because the controller layer should be used to get the requests, send responses and call Services to run Business logic. Save a user is a service layer task.
Depending on project's requirement, we want to change from JdbcTemplate into NamedParameterJdbcTemplate in database handling. But I got null point error when I test my coding. I'm sure the error has occurred because of my configuration, but I could not resolve it.
Config
#Value("${spring.datasource.driver-class-name}")
protected String driver;
#Value("${spring.datasource.url}")
protected String url;
#Value("${spring.datasource.username}")
protected String user;
#Value("${spring.datasource.password}")
protected String pass;
#Bean
public DataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(pass);
dataSource.setTestOnBorrow(true);
dataSource.setTestOnReturn(true);
dataSource.setTestWhileIdle(true);
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
#Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(){
return new NamedParameterJdbcTemplate(dataSource);
}
DaoImpl
#Repository
public class DAOImpl implements DAO {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Override
public List<Type> findAllType() {
String sql = "select * from type";
RowMapper<Type> rm = new TypeRowMapper();
List<Type> list = jdbcTemplate.query(sql, rm);
return list;
}
}
Test
public class Test {
#Autowired
protected DAOImpl dao;
public static void main(String[] args) {
List<Type> list = new ArrayList<Type>();
Test test = new Test();
System.out.println(test.dao);
list = test.dao.findAllType();
for(Type type : list){
System.out.println(type.getName());
}
}
}
Stacktrace
null
Exception in thread "main" java.lang.NullPointerException
at com.example.dao.Test.main(Test.java:18)
I'm trying to set my user on to a course object that I created and I keeping getting the error org.springframework.security.core.userdetails.User cannot be cast to com.example.security.CustomUserDetails I know I'm getting the user details from the session, because when I run it in debug mode I can see the name of the currently logged in user, so I think I'm somewhat close to a solution.
Here's my Controller
#RequestMapping(value="createCourse", method=RequestMethod.POST)
public String createCoursePost (#ModelAttribute Course course, Long userId, ModelMap model, Authentication auth)
{
CustomUserDetails myUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
userId = myUserDetails.getUser().getId();
User user = userRepo.findOne(userId);
course.setUser(user);
courseRepo.save(course);
return "redirect:/courses";
}
Here's my UserDetailsServiceImpl
#Service
#Qualifier("customUserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepo;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.example.domain.User user = userRepo.findByUsername(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.example.domain.User user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRoleName()));
}
return new ArrayList<GrantedAuthority>(setAuths);
}
Here's my customUserDetails, it's possible that me getting and setting the user could be redundant, but I saw an example with this, and so I'm not really sure what to do with this.
public class CustomUserDetails extends User implements UserDetails{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
private static final long serialVersionUID = 2020921373107176828L;
public CustomUserDetails () {}
public CustomUserDetails (User user) {
super(user);
}
#Override
public Set<Authorities> getAuthorities() {
return super.getAuthorities();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Here's my webSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
#Autowired
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(csrfTokenRepository());
http
.authorizeRequests()
.antMatchers("/", "/home", "/register", "/courses", "/editCourse", "/sets", "/search", "/viewCourse/{courseId}", "/fonts/glyphicons-halflings-regular.ttf","fonts/glyphicons-halflings-regular.woff", "fonts/glyphicons-halflings-regular.woff", "/viewCourse/post/{postId}").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/loggedout")
.and()
.sessionManagement()
.maximumSessions(1);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
if(encoder == null) {
encoder = new BCryptPasswordEncoder();
}
return encoder;
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
}
I solved this just in case anyone else sees this. I think my problem was that I wasn't returning userdetails in the userdetailsimpl class. And I also had to update my controller. So here's my new code
Controller
#RequestMapping(value="createCourse", method=RequestMethod.POST)
public String createCoursePost (#ModelAttribute Course course, Long userId, ModelMap model)
{
CustomUserDetails myUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
userId = myUserDetails.getUser().getId();
User user = userRepo.findOne(userId);
course.setUser(user);
courseRepo.save(course);
return "redirect:/courses";
}
And userDetailsServiceImpl
#Service
#Qualifier("customUserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepo;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.example.domain.User user = userRepo.findByUsername(username);
CustomUserDetails customUserDetails = new CustomUserDetails(user);
customUserDetails.setUser(user);
return customUserDetails;
}
}
I have created an implementation of HandlerMethodArgumentResolver to return the current logged in user for controller methods annotated with #CurrentUser. However, when the resolveArgument method of the HandlerMethodArgumentResolver gets called, an empty user is returned. I have verified that my custom UserDetailsService implementation does retrieve a full user object.
The code I'm using is as follows.
The #CurrUser annotation:
#Target({ElementType.PARAMETER, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#AuthenticationPrincipal
public #interface CurrentUser {
}
The HandlerMethodArgumentResolver implementation with empty user:
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Inject
private UserService userService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(CurrentUser.class) != null
&& parameter.getParameterType().equals(User.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(parameter)) {
Principal principal = webRequest.getUserPrincipal();
User user = (User) ((Authentication) principal).getPrincipal(); // This user is empty!!!
return user;
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
The calling Controller method:
#RequestMapping(value = "/user", method = RequestMethod.GET)
public HttpEntity<Resource<User>> currentUser(#CurrentUser User self) {
log.debug("CurrentUserController > currentUser GET> " + self);
}
The loadUserByUsername of UserDetailsService that retrieves the populated user (I verified this is called first):
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
CustomUserDetails ud = new CustomUserDetails(user);
return ud;
}
The WebMvcConfigurerAdapter configuration:
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserMethodArgumentResolver());
}
#Bean
public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
return new CurrentUserMethodArgumentResolver();
}
I discovered what the issue was - the problem was in how I implemented `CustomUserDetails':
public static class CustomUserDetails extends User implements UserDetails {
...
}
I was extending my custom User class and implementing UserDetails. I'm not sure why, but Spring didn't like this. I since fixed the code by creating and populating a new org.springframework.security.core.userdetails.User, and returning this from the call to `loadUserByUsername'.