This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 2 years ago.
I am working on my first spring boot project and I am having trouble with testing my class as a Mockito newbie.
I need to check that the findAndUpdateUser method below update User entity fields correctly :
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepo;
public User findUser(String id) {
return userRepo.findById(id);
}
public User findAndUpdateUser(String id) {
User foundUser = findUser(id);
//some update on foundUser fields
return foundUser;
}
}
To test, I need to mock entity bean because I don't have DB locally. Here is my test class :
#RunWith(MockitoJUnitRunner.class)
public class UpdateFieldsTest {
#Mock
private UserRepository userRepo;
#Test
public void test1() {
User user = new User();
user.setId("testId");
//setting some other user fields
Mockito.when(userRepo.findById("testId")).thenReturn(user);
UserServiceImpl userService = new userServiceImpl();
User returnedUser = userService.findAndUpdateUser("testId");
//Checking if user field values are updated after findAndUpdate call
Assert.xxx
//other asserts
}
}
But this code throw me some NullPointerException on method call. I even tried to mock findUser method but I still have error...
Am I having a bad approach ? How should I test my method ?
Any help is welcomed
Thanks all
using this instantiation UserServiceImpl userService = new userServiceImpl(); make the UserRepo null;
you are using mockito, so you can use
#InjectMocks
private UserServiceImpl; and delete the instastiation line.
Mocks won't magicly appear inside of your object. You need to inject them manually. In you test, inside of the userService, the userRepo will be null. You inject dependancies either by constructor or by setter. It would look like this in your test:
UserServiceImpl userService = new userServiceImpl(userRepo);
Or
UserServiceImpl userService = new userServiceImpl();
userService.setUserRepo(userRepo);
Related
I'm learning how to test controllers and I still don't understand some things. I've watched some tutorials and I read some pages to learn but I don't understand 100%. I tried to do it but I don't think it's correct. I'm using Spring Boot and Mock.
This is my controller class:
#Autowired
private UserDetailsServiceImpl userService;
#Autowired
private ShopService shopService;
#Autowired
private GameService gameService;
#GetMapping(value = "/userProfile")
public String userProfile(final Model model, final HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
User us = null;
if (principal instanceof User) {
us = (User) principal;
}
model.addAttribute("user", us);
model.addAttribute("teamsCreated", this.userService.teamsCreated(us));
model.addAttribute("teamshejoined", this.userService.teamsHeJoined(us));
model.addAttribute("gamesheplays", this.gameService.findGamesHePlays(us.getId()));
model.addAttribute("groupgamesheplays", this.gameService.findGroupHePlays(us));
model.addAttribute("prizehewon", this.gameService.getPrizeHeWon(us));
return "user/profile";
}
This is my test class:
#Autowired
private MockMvc mockMvc;
#MockBean
private UserDetailsServiceImpl userService;
#MockBean
private ShopService shopService;
#MockBean
private UserRepository userRepository;
#MockBean
private DataUserRepository dataUserRepository;
private User user;
private DataUser dataUser;
private Team team;
private Set<User> people;
#BeforeEach
void setUp() {
user = new User();
user.setId(12L);
user.setUsername("usertest");
user.setPassword("Pass1234");
dataUser.setId(915495L);
dataUser.setMoney(1);
dataUser.setUser(user);
user.setDataUser(dataUser);
people = new HashSet<>();
people.add(user);
team = new Team();
team.setId(2L);
team.setName("team1");
team.setCreator(user);
team.setPeople(people);
team.setCreationDate(Date.from(Instant.now()));
}
#WithMockUser(value = "spring")
#Test
void testProcessCreationUser() throws Exception{
mockMvc.perform(post("/userProfile")
.with(csrf())
.param("username", "usertest")
.param("password", "Pass1234")
.param("id", "49L")
.param("dataUser", ?????? )) // the ?? is because I dont know how to add a dataUser type, I only know Strings
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("users/createClienteForm"));
}
I tried doing it seeing previous methods some people made and with what I read before in notes I took, but i don't really understand so if you could tell me if that's correct or what is wrong and I could do better and I'd appreciate it. Thank you so much!
First of all, you're trying to mock a #GetMapping as a post() and it will not work.
Second, it seems that your resource does not need any parameter as it only gathers information about the user that is logged in your system, am I correct?
So, your test should look like:
#WithMockUser(value = "spring")
#Test
void testProcessCreationUser() throws Exception{
mockMvc.perform(get("/userProfile")
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("users/createClienteForm"));
}
You should review to which page your user will be redirected at the end, as
users/createClienteForm doesn't seem to be the corrected one in your mock.
Also, always choose a name for your test that correctly explains what your test is doing/verifying. I think testShowUserProfile should be fine in your case.
I've been using some previous examples of testing methods some of my work partners used and they didn't have any problem but when i use them for this project it doesn't work at all. One of my partners saw the methods and he didn't know either what was wrong.
This is my test class:
#Mock
UserRepository dataRepository;
#Autowired
protected UserService userService;
private User u;
#BeforeEach()
void setUp() {
u = new User();
u.setId(23L);
u.setUsername("test");
u.setPassword("Pass1234.");
u.setInfo(null);
this.dataRepository.save(u);
}
#Test
void testLoadUserByUsername() throws Exception {
when(dataRepository.findByUsername(anyString())).thenReturn(u);
userService.loadUserByUsername("test");
assertEquals(u, userService.loadUserByUsername(anyString()));
verify(dataRepository).findByUsername(anyString());
}
#Test
void testFindAllUsers() throws Exception {
List<User> user = this.userService.findAllUsers();
Assertions.assertNotNull(user);
}
}
When i execute the test i get the same trace every time and it is, for the first method:
org.springframework.security.core.userdetails.UsernameNotFoundException: User can't be found
at com.project.UserService.loadUserByUsername(UserService.java:41)
I don't know what could it be because i'm searching the same name i'm setting just a few lines above so if u could help me i would appreciate it.
The UserService class is:
#Autowired
private BCryptPasswordEncoder passw;
#Autowired
private UserRepository userRepository;
#Autowired
private DataRepository dataRepository;
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User can't be found");
}
return user;
}
public List<User> findAllUsers() {
List<User> l = new ArrayList<User>();
l = (List<User>) this.userRepository.findAll();
return l;
}
public void save(final User u) {
this.userRepository.save(u);
}
}
And the line of the code that is pointed by the exception is:
userService.loadUserByUsername("test");
Thank u so much.
the problem is with your #Mock. Basically, you are mocking an object in your test class and then pretending the #Autowired object in your UserService class to be mocked. What you are missing here is the instruction that tells Spring to put your mocked object into your UserService class. So here is the upgrade that will fix it.
Your test class becomes like this:
#Mock
UserRepository dataRepository;
#InjectMocks
#Autowired
protected UserService userService;
private User u;
#BeforeEach()
void setUp() {
u = new User();
u.setId(23L);
u.setUsername("test");
u.setPassword("Pass1234.");
u.setInfo(null);
this.dataRepository.save(u);
MockitoAnnotations.initMocks(this);
}
// Here goes the remaining code as it is...
Basically what I did was to add the #InjectMocks annotation above the #Autowired element that needs to receive your mock. Furthermore, with MockitoAnnotations.initMocks(this) I have initialized the mocks.
NOTE: the line where you do this.dataRepository.save(u); is useless. The point is that every method you call on a Mocked object is not executed, therefore there is no difference in having it or not. The only meaningful line is when(dataRepository.findByUsername(anyString())).thenReturn(u); that you have below.
PS. The dependency that contains #InjectMocks and MockitoAnnotations is the following:
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
Wish you a great day,
D
I have a spring boot application where, suddenly, the autowired services returning null at specific methods.
Here is a code snippet for the controller:
#RestController
#RequestMapping("/user")
#CrossOrigin
public class UserManagementController {
#Autowired
private UserService userService;
...
#PostMapping("/register")
private GameInfo register(#RequestBody UserInfo userInfo) {
User user = new User();
...
user.setUsername("user-" + userService.count());
...
return gameController.getGameInfo(user);
}
...
#PostMapping("/statistics")
public StatisticsInfo statistics(
#RequestParam(name = "username", required = true) String username,
Authentication authentication) {
User user = userService.findByUsername(username);
...
}
}
The userService is null in the first method, and works fine in the second one.
UPDATE:
Here is the code snippet for the UserService which is null in "register" method.
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository repository;
public long count() {
return repository.count();
}
...
}
The userService has normal CRUD methods.
Kindly note that the userService is null, so a NullPointerException is thrown when I invoke any of its methods.
UPDATE 2:
I have created a clone of the faulty method register and named it dontRegister, this magically solved the issue of register but introduced the same issue in the new method.
I don't understand the logic behind this, as it seems that I always need to add one extra unused method.
I'll keep the question open until maybe someone comes up with an explanation.
Your register method is private, make it public. Logically it should be public anyway as it is called from outside the package.
My ServiceImpl looks like this:
#Service
public class GuildUsersServiceImpl implements GuildUsersService {
#Autowired
private final GuildUsersRepository guildUsersRepository;
#Autowired
private GuildService guildService;
#Autowired
private UserService userService;
#Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
#Autowired
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}
And my service test for create looks like this:
#RunWith(SpringRunner.class)
public class GuildUsersServiceTest {
#Mock private GuildUsersRepository guildUsersRepository;
#Mock private UserService userService;
#Mock private GuildService guildService;
#Mock private GuildUserRoleTypeService guildUserRoleTypeService;
#InjectMocks private GuildUsersServiceImpl guildUsersService;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void createTest() {
GuildUsers guildUsers = mockGuildUsers();
when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers);
GuildUsers dbGuildUsers = guildUsersService.create(guildUsers);
assertEquals(dbGuildUsers.getId(),guildUsers.getId());
}
}
I am using jUnit4. Despite using when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers); in the test, I run into a Null Pointer Exception with the following trace:
java.lang.NullPointerException: Cannot invoke "com.project.entities.domain.GuildUsers.getId()" because "dbGuildUsers" is null
I suspect something is wrong with how I have mocked the #Autowired classes. What needs to be done differently while mocking classes in the Test class ?
At the same time I want the services in the following test case to work as well :
#Test
public void createTest1() {
GuildUsers guildUsers = mockGuildUsers();
GuildUsersWithoutGuildIdRequestDTO guildUsersWithoutGuildIdRequestDTO = new GuildUsersWithoutGuildIdRequestDTO();
guildUsersWithoutGuildIdRequestDTO.setUserId(guildUsers.getUser().getId());
guildUsersWithoutGuildIdRequestDTO.setGuildUserRoleTypeId(guildUsers.getGuildUserRoleType().getId());
when(guildService.get(guildUsers.getGuild().getId())).thenReturn(Optional.of(guildUsers.getGuild()));
when(guildRepository.findById(any())).thenReturn(Optional.of(guildUsers.getGuild()));
when(userService.get(guildUsers.getUser().getId())).thenReturn(Optional.of(guildUsers.getUser()));
when(guildUsersRepository.findById(guildUsers.getId())).thenReturn(null);
when(guildUserRoleTypeService.get(guildUsers.getGuildUserRoleType().getId())).thenReturn(Optional.of(guildUsers.getGuildUserRoleType()));
when(guildUsersRepository.save(any())).thenReturn(guildUsers);
GuildUsersResponseDTO dbGuildUsersResponseDTO =
guildUsersService.create(guildUsers.getGuild().getId(),guildUsersWithoutGuildIdRequestDTO);
assertEquals(dbGuildUsersResponseDTO.getGuildUserRoleType(),guildUsers.getGuildUserRoleType());
}
I don't know why you did field injection as well as constructor injection at the same time. You should remove one of them first, for instance:
#Service
public class GuildUsersServiceImpl implements GuildUsersService {
//#Autowired - removed
private final GuildUsersRepository guildUsersRepository;
#Autowired
private GuildService guildService;
#Autowired
private UserService userService;
#Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
//#Autowired - removed
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}
OR remove the constructor to add:
#Autowired
private final GuildUsersRepository guildUsersRepository;```
InjectMock documentation clearly says: "Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below."
In your case, it apparently chooses property injection, hence guildUserRoleTypeService remains uninitialised. Mixing property and constructor injection is not a good idea as pointed above.
To improve the general design and testability of your GuildUsersServiceImpl, I would suggest to stick with constructor injection. This way you can:
a) Declare all the fields as private final, so they will always be set once the object is created, avoiding race conditions and NPEs.
b) Initialise GuildUsersServiceImpl in the test using a proper constructor rather than InjectMocks.
Leaving this here for other people that also wasted an afternoon on trying to get a repository to return what they configured. What ended up being the thing I had to do is rather than annotating the repository with #Mock I had to use the #MockBean annotation for the Mockito/Spring combination to work properly.
As per javadoc of #MockBean:
When #MockBean is used on a field, as well as being registered in the application context, the mock will also be injected into the field.
I'm writing junit and I using #mock and #injectMock.But,I find #injectMocks doesn't work when bean in spring aop.code like this:
QuestionService.java:
#Component
public class QuestionService implements IQuestionService{
#Resource
private IUserService userService;
#Override
public User findUserById(long id) {
// TODO Auto-generated method stub
User user = userService.findUserById(id);
return user;
}
}
Test.java:
#Mock
IUserService mockuserService;
#InjectMocks
#Resource
QuestionService questionService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testfind() {
when(mockuserService.findUserById(1)).thenReturn(
new User(1, "name"));
User user = questionService.findUserById(1);
Assert.assertEquals(new User(1, "name"), user);
}
It works!
But,when I add userService in spring aop,It does not work!
For example, transaction aop.
How can I fix it?
I found an interesting behavior - once I used the AOP around any method in the class, the mocks stopped working; instead the 'real' component was initiated although there was no code for that matter.
I found that if you were to use #MockBean - everything works.
Why did you annotate QuestionService with #Resource in test class? Are you running with SpringJUnit4ClassRunner by loading bean configs? If not remove #Resource annotation and try, doesn't matter whether using AOP or not, it should work.
And add below snippet of code in #Before method of your test class as first line.
MockitoAnnotations.initMocks(this);
#InjectMocks : Mark a field on which injection should be performed.
MockitoAnnotations.initMocks(this): initializes fields annotated with Mockito annotations.