#RestController
#RequestMapping("/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
System.err.println(userService);
this.userService = userService;
System.err.println(this.userService);
}
#PostMapping("/login")
private ResponseEntity<ServiceResponse<UserResp>> login(#RequestBody UserReq userReq) {
System.err.println(userService);
return ResponseEntity.ok(userService.login(userReq));
}
userService is null inside the method, but it is setting inside the constructor
Cannot invoke "mscore.service.UserService.login(mscore.model.req.UserReq)" because "this.userService" is null
Related
I am writing a jUnit5 test to test my getUserByEmail(String email) using Mockito and when running the test, it is failing.
#SpringBootTest
#ExtendWith(MockitoExtension.class)
class UserServiceTest {
#MockBean
private UsersRepository userRepository;
#Autowired
private UserService userService;
#Test
void check_email_existInDB_thenReturn_True() {
// given - precondition or setup
UserDetails userdetails2 = new UserDetails(101L, "Anthony Ji", "anthony#gmail.com", "password2");
userRepository.save(userdetails2);
// when - action or behaviour that we are going test
when(userRepository.save(userdetails2)).thenReturn(userdetails2);
// then - verify the result or output using assert statements
assertEquals(userdetails2.getEmail(), userService.getUserByEmail("anthony#gmail.com"));
//assertTrue(userService.getUserByEmail(userdetails2.getEmail()));
}
}
This is my interface with extending of JPARepository
public interface UsersRepository extends JpaRepository<UserDetails, Long>{
}
This is my service level class
#Service
public interface UserService {
public Optional<UserDetails> getUserByEmail(String email);
public UserDetails saveUserDetails(UserDetails userDetails);
}
And this is the serviceImpl Class..
#Service
public class UserServiceImpl implements UserService{
#Autowired
private UsersRepository userRepository;
#Override
public Optional<UserDetails> getUserByEmail(String email) {
List<UserDetails> allUsers = this.userRepository.findAll();
Optional<UserDetails> userInfo = allUsers.stream().filter(user -> user.getEmail().equalsIgnoreCase(email)).findAny();
return userInfo;
}
#Override
public UserDetails saveUserDetails(UserDetails userDetails) {
UserDetails savedUserDetails = this.userRepository.save(userDetails);
return savedUserDetails;
}
}
and finally, this is my controller class while using jersey framework.
#Path(value = "/user")
public class RegistrationResources {
private static Logger logger = LoggerFactory.getLogger(RegistrationResources.class);
#Autowired
private UserService userService;
#POST
#Path("/registration")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response registerUser(UserDetails userDetails, #Context UriInfo uriInfo) {
logger.info("Inside the registration resource and registering the user");
boolean isEmailExists = isEmailExists(userDetails.getEmail());
if(isEmailExists) {
logger.info("Email Already Registered");
return Response.status(404).entity("Email Address already registered").build();
}
else {
this.userService.saveUserDetails(userDetails);
logger.info("User successfully registered");
return Response.status(200).entity("User successfully registered").build();
}
}
public boolean isEmailExists(String email) {
UserDetails userByEmail = this.userService.getUserByEmail(email).orElse(null);
if(userByEmail == null) {
return false;
}
return true;
}
I tried as above explained in the code.
I am new to unit testing REST API in Spring Boot.
I am expecting response status as CREATED but instead I am getting a PAGE NOT FOUND error.
Below is the code for:-
UserControllerUnitTests
#SpringBootTest
#ContextConfiguration(classes = { CommonConfig.class, SecurityConfig.class})
#RunWith(SpringRunner.class)
class UserControllerUnitTests {
private static ObjectMapper mapper;
private static final String URI = "/users";
MockMvc mvc;
#Autowired
WebApplicationContext webAppContext;
#Mock
UserService userService;
MvcResult mvcResult;
#BeforeAll
static void setUp() {
mapper = new ObjectMapper();
}
#BeforeEach
void initialize() throws Exception {
mvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
....
....
....
void shouldReturnStatusCreatedIfValidUserPassedForPostUser(long index) throws Exception {
int expectedStatus = HttpStatus.CREATED.value();
UserDAO returnUser;
UserDAO user = userList.get(index);
userList.remove(index);
String jsonContent = mapper.writeValueAsString(user);
user.setId(index);
user.setEncryptedPassword(null);
Mockito.when(userService.addUser(Mockito.any())).thenReturn(user);
mvcResult = mvc.perform(MockMvcRequestBuilders.post(URI)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonContent)).andReturn();
//Mockito.verify(userService, Mockito.times(1)).addUser(Mockito.any());
int actualStatus = mvcResult.getResponse().getStatus();
Assert.assertEquals("Response status should be CREATED", expectedStatus, actualStatus);
jsonContent = mvcResult.getResponse().getContentAsString();
returnUser = mapper.readValue(jsonContent, UserDAO.class);
Assert.assertEquals("EncryptedPassword should not be returned", null,
returnUser.getEncryptedPassword());
}
User Controller.class
#RestController
#RequestMapping("users/")
public class UserController {
UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
....
....
....
#PostMapping(path = "",
consumes = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON },
produces = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public ResponseEntity<UserDAO> createUser(#Valid #RequestBody UserDAO user) {
String password = user.getEncryptedPassword();
user.setEncryptedPassword(null);
UserDAO retreivedUser;
if(user.getId() != 0)
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
user.setEncryptedPassword(password);
retreivedUser = userService.addUser(user);
if(retreivedUser != null)
return new ResponseEntity<>(retreivedUser, HttpStatus.CREATED);
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}
The full code can be found at https://github.com/vineethmaller/springboot-userservice
I spotted a few errors:
Get rid of ContextConfiguration
#SpringBootTest
// #ContextConfiguration(classes = { CommonConfig.class, SecurityConfig.class})
#RunWith(SpringRunner.class)
class UserControllerUnitTests {
Specify correct mapping on the controller (no slash)
#RestController
#RequestMapping("users")
public class UserController {
You setup your UserService mock which is not used in the test. Did you mean #MockBean?
#MockBean
UserService userService;
#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
}
#Service
class UserServiceBuilder(){
public UserService build(ConfigBean config){
UserService s = new UserServcie()
s.name = config.xxxx
//other config omitted
return s;
}
}
#Configuration
class AppConfig{
#Bean
public UserService UserService(UserServiceBuilder builder, ConfigBean configBean) {
return builder.load(configBean);
}
}
class UserCtrl {
#Autowired
private UserService UserService; // get null when the #Cachable
}
UserService is created by the UserServiceBuilder which will read a log of properties from the config file.
Then the UserService will be injected to UserCtrl, it works at first.
However once I add the #Cachable to one method of UserService, all the fileds of the injected UserService will be null.
It seems like that spring will create a proxy of UserService when using cache, and the proxy object does not have the fileds.
How to fix that?
Yes, you are right it's because of the proxy. You must add getters in the UserService and use this getters if you want take the fields of the UserService outside.
#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
//ADD THIS:
public String getName() {
return this.name;
}
}
But if you add output inside UserService method:
public User getUser(String name) {
System.out.println("PING " + this.name);
...
}
you will see that this.name inside object is not null.
P.S. and I think that you can remove #Service annotation from UserService. Because you have #Service and #Bean registration of UserService. It's confusing.
I try to make a dummy test of a method in my controller but doesn't work , i have a null pointer exception and i would like to know why it doesn't work, i precise that's my first time when i make this kind of tests.
This is my rest controller :
#RestController
#RequestMapping("/api")
public class AdminController {
#Autowired
private UserService userService;
#Secured(value = "ROLE_ADMIN")
#GetMapping("/roles")
public ResponseEntity<List<Role>> getRoles(){
return new ResponseEntity(userService.getRoles(), HttpStatus.OK);
}
This my controller test :
#RunWith(SpringRunner.class)
#WebMvcTest(value = AdminController.class, secure = false)
public class AdminControllerTest {
private MockMvc mvc;
#MockBean
UserService userService;
#Test
public void getRoles() throws Exception{
Role role=new Role("Admin");
List<Role> roles=Arrays.asList(role);
given(userService.getRoles()).willReturn(roles);
mvc.perform(get("/api/roles")).andExpect(status().isOk());
}
When i run the test for this method a i have this kind of error :
Thank you in advance for your help
Try this. It should work. I think you missed #Autowired annotation
#RunWith(SpringRunner.class)
#WebMvcTest(value = AdminController.class, secure = false)
public class AdminControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
UserService userService;
#Test
public void getRoles() throws Exception{
Role role=new Role("Admin");
List<Role> roles=Arrays.asList(role);
given(userService.getRoles()).willReturn(roles);
mvc.perform(get("/api/roles")).andExpect(status().isOk());
}
}
I'm trying to test My Spring controllers using Mockito, but I can't actually get how can I do that without making everything #Mock.
Moreover test method returns me NullPointerException, as it can see no user and actually no user role at all.
Is there a way to test my controllers somehow?
(Controller class)
#Controller
#SessionAttributes("user")
#RequestMapping("/login.htm")
public class LoginController{
#Autowired
private UserDao userDao;
#Autowired
private LoginValidator loginValidator;
public LoginValidator getLoginValidator() {
return loginValidator;
}
public void setLoginValidator(LoginValidator loginValidator) {
this.loginValidator = loginValidator;
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#RequestMapping(method = RequestMethod.GET)
public String getSendEmptyForm(ModelMap model, HttpServletRequest req) {
req.getSession().invalidate();
model.addAttribute("loginForm", new LoginForm());
return "login";
}
#RequestMapping(method = RequestMethod.POST)
public String postSubmittedForm(ModelMap model, #ModelAttribute("loginForm") LoginForm loginForm,
BindingResult result, SessionStatus status) {
//validate form
loginValidator.validate(loginForm, result);
if (result.hasErrors()) {
return "login";
}
User user = userDao.findByLogin(loginForm.getLogin());
model.addAttribute("user", user);
if (user.getRole().getName().equals("Admin")) {
model.addAttribute("usersList", userDao.findAll());
return "viewAllUsersPage";
}
if (user.getRole().getName().equals("User")){
return "userPage";
}
model.addAttribute("error", "Your role is not User or Admin");
return "errorPage";
}
}
And my testing class
#RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
#InjectMocks
private LoginController controllerUT = new LoginController();
#Mock
private ModelMap model;
#Mock
private LoginForm loginForm;
#Mock
private BindingResult result;
#Mock
private SessionStatus status;
#Mock
private LoginValidator validator;
#Mock
private UserDao userDao;
#Mock
private User useк;
#Test
public void testSendRedirect(){
final String target = "login";
String nextPage = controllerUT.postSubmittedForm(model, loginForm, result, status);
assertEquals(target, nextPage);
}
}
First off you seem to be missing stubbing for loginForm.getLogin() and userDao.findByLogin(loginForm.getLogin()) and user.getRole().getName(). Without such stubbing, these methods called on a mock will return a default value (i.e. null).
So you may want to add :
when(loginForm.getLogin()).thenReturn(login);
when(userDao.findByLogin(login)).thenReturn(user);
when(user.getRole()).thenReturn(role);
when(role.getName()).thenReturn("Admin");
You will want to vary the return values for different tests.
Depending on your implementation classes for User and Role, you could simply supply real instances.
For a test that simulates the result to have errors you'll want to add this stubbing :
when(result.hasErrors()).thenReturn(true);
since otherwise the default false is returned.