Mocking fails for some reason

@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
class UserServiceTests {

User testUser = new User(
1L,
"xxxJohnxxx",
"John Doe",
"Password123"
);

@Mock
UserRepository userRepository;

@Autowired
@InjectMocks
UserService userService;

@BeforeEach
void setUp() {
when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));
when(userRepository.findById(2L)).thenReturn(Optional.empty());
}

@Test
void testFindUserById() {
assertThat(userService.findUserById(1L)).isNotEmpty().hasValueSatisfying(usr -> {
assertThat(usr.getUsername()).isEqualTo("xxxJohnxxx");
});
assertThat(userService.findUserById(2L)).isEmpty();
}
}
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
class UserServiceTests {

User testUser = new User(
1L,
"xxxJohnxxx",
"John Doe",
"Password123"
);

@Mock
UserRepository userRepository;

@Autowired
@InjectMocks
UserService userService;

@BeforeEach
void setUp() {
when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));
when(userRepository.findById(2L)).thenReturn(Optional.empty());
}

@Test
void testFindUserById() {
assertThat(userService.findUserById(1L)).isNotEmpty().hasValueSatisfying(usr -> {
assertThat(usr.getUsername()).isEqualTo("xxxJohnxxx");
});
assertThat(userService.findUserById(2L)).isEmpty();
}
}
userService.findUserById(1L) fails and returns empty optional, any idea why?
74 Replies
JavaBot
JavaBot5mo ago
This post has been reserved for your question.
Hey @Koblížkáč! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically closed after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
tjoener
tjoener5mo ago
You don't need to isNotEmpty, the hasValue... already checks that Show the UserService Also this should definitely not be a SpringBootTest
JavaBot
JavaBot5mo ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
Koblížkáč
KoblížkáčOP5mo ago
sorry im kinda new to all this testing shit, so im just tryint to figure this out, id be really glad if you showed me the way how to do this lol user service is:
@Service
@Validated
public class UserService {

private final UserRepository userRepository;
private final BCryptPasswordEncoder passwordEncoder;

public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}

public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}

/**
* Saves a user to the database, the provided password should not
* be handed as hashed, as it will be hashed automatically.
* @param user User
* @return User
* @throws ConstraintViolationException When some of the fields are not valid
*/
@Transactional
public User save(@Valid User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
}
@Service
@Validated
public class UserService {

private final UserRepository userRepository;
private final BCryptPasswordEncoder passwordEncoder;

public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}

public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}

/**
* Saves a user to the database, the provided password should not
* be handed as hashed, as it will be hashed automatically.
* @param user User
* @return User
* @throws ConstraintViolationException When some of the fields are not valid
*/
@Transactional
public User save(@Valid User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
}
tjoener
tjoener5mo ago
ok, so you should inject PasswordEncoder not BCryptPasswordEncoder You should have a config bean for PasswordEncoder that returns BCryptPasswordEncoder So you use the same one everywhere
Koblížkáč
KoblížkáčOP5mo ago
well, thats just a little thing, im more worried about why the test isnt passing
tjoener
tjoener5mo ago
Remove all those annotations from the test And just run with @ExtendWith(MockitoExtension.class) From the test class
Koblížkáč
KoblížkáčOP5mo ago
it doesnt let me autowire the userservice then
tjoener
tjoener5mo ago
That doesn't make sense. This is how I always do it Show the new test code
Koblížkáč
KoblížkáčOP5mo ago
@ExtendWith(MockitoExtension.class)
class UserServiceTests {

User testUser = new User(
1L,
"xxxJohnxxx",
"John Doe",
"Password123"
);

@Mock
UserRepository userRepository;

@Autowired
@InjectMocks
UserService userService;

@BeforeEach
void setUp() {
when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));
when(userRepository.findById(2L)).thenReturn(Optional.empty());
}

@Test
void testFindUserById() {
assertThat(userService.findUserById(1L)).hasValueSatisfying(usr -> {
assertThat(usr.getUsername()).isEqualTo("xxxJohnxxx");
});
assertThat(userService.findUserById(2L)).isEmpty();
}

@Test
void testFindUserByUsername() {
when(userRepository.findByUsername("xxxJohnxxx")).thenReturn(Optional.of(testUser));
assertThat(userService.findUserByUsername("xxxJohnxxx")).isNotEmpty();
assertThat(userService.findUserByUsername("Johnxxx")).isEmpty();
}

@Test
void testSave() {
User userWrong = User.builder()
.username("Johnxxx")
.name("John Doe")
.email("john.doe").password("Password1").build();
User userCorrect = User.builder()
.username("Johnxxx")
.email("[email protected]").password("Password1").build();
assertThatThrownBy(() -> userService.save(userWrong))
.isInstanceOf(ConstraintViolationException.class);
assertThat(userService.save(userCorrect)).isNotNull();
}
}
@ExtendWith(MockitoExtension.class)
class UserServiceTests {

User testUser = new User(
1L,
"xxxJohnxxx",
"John Doe",
"Password123"
);

@Mock
UserRepository userRepository;

@Autowired
@InjectMocks
UserService userService;

@BeforeEach
void setUp() {
when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));
when(userRepository.findById(2L)).thenReturn(Optional.empty());
}

@Test
void testFindUserById() {
assertThat(userService.findUserById(1L)).hasValueSatisfying(usr -> {
assertThat(usr.getUsername()).isEqualTo("xxxJohnxxx");
});
assertThat(userService.findUserById(2L)).isEmpty();
}

@Test
void testFindUserByUsername() {
when(userRepository.findByUsername("xxxJohnxxx")).thenReturn(Optional.of(testUser));
assertThat(userService.findUserByUsername("xxxJohnxxx")).isNotEmpty();
assertThat(userService.findUserByUsername("Johnxxx")).isEmpty();
}

@Test
void testSave() {
User userWrong = User.builder()
.username("Johnxxx")
.name("John Doe")
.email("john.doe").password("Password1").build();
User userCorrect = User.builder()
.username("Johnxxx")
.email("[email protected]").password("Password1").build();
assertThatThrownBy(() -> userService.save(userWrong))
.isInstanceOf(ConstraintViolationException.class);
assertThat(userService.save(userCorrect)).isNotNull();
}
}
Koblížkáč
KoblížkáčOP5mo ago
No description
tjoener
tjoener5mo ago
yes, don't autowire it Just InjectMocks OK, little bit of explanation you seem to be missing You're trying to test business logic, in this case a service So to test it, it might need dependencies, like your UserRepository. So the ONLY thing you need, is a mock for the repository, that returns the right stuff you need to test your userservice So you don't need any other spring stuff This isn't true for other types of tests, for example, testing the HTTP api, or the database, because they need specific spring stuff to test And you should test those with specific test slices https://docs.spring.io/spring-boot/appendix/test-auto-configuration/slices.html Instead of SpringBootTest Because that will start your entire spring application basically, for no good reason
Koblížkáč
KoblížkáčOP5mo ago
hmm yes that really makes sense, its now almost working as expected, but its throwing NPE because passwordencoder is null for some reason
tjoener
tjoener5mo ago
yes, you didn't mock it...
Koblížkáč
KoblížkáčOP5mo ago
so now its loaded only what is mocked? if i understand correctly
tjoener
tjoener5mo ago
yes
Koblížkáč
KoblížkáčOP5mo ago
so i suppose i should make separate configuration and load it
tjoener
tjoener5mo ago
no UNIT test Just mock it That's also why I said use PasswordEncoder Because that's an interface
Koblížkáč
KoblížkáčOP5mo ago
right, but how do i mock it if i dont know what will it would return?
tjoener
tjoener5mo ago
It doesn't matter for your test what it returns
Koblížkáč
KoblížkáčOP5mo ago
so like when(passwordEncoder.encode("Password1")).thenReturn("hashedPassword"); is good enough?
tjoener
tjoener5mo ago
What are you doing when you do that? You don't need hashedPAssword do you?
Koblížkáč
KoblížkáčOP5mo ago
i placed it into the saving test
No description
tjoener
tjoener5mo ago
Yeah, you don't use hashedPassword So remove the when haha, and userWrong and userCorrect is exactly why I hate builders Goodbye compile time safety
Koblížkáč
KoblížkáčOP5mo ago
thats why i like kotlin
tjoener
tjoener5mo ago
You made a builder
Koblížkáč
KoblížkáčOP5mo ago
yes
tjoener
tjoener5mo ago
Just don't make a builder... Constructors exist You're using builders as a vague way to get named parameters
Koblížkáč
KoblížkáčOP5mo ago
... you told me to mock it, and now telling me to remove the mock?
tjoener
tjoener5mo ago
mocks return default values But a mock is not null So you have a passwordencoder at that point so your service doesn't call a method on a null object You don't need to give it logic, unless you need to
Koblížkáč
KoblížkáčOP5mo ago
so adding
@Mock
PasswordEncoder passwordEncoder;
@Mock
PasswordEncoder passwordEncoder;
is enough?
tjoener
tjoener5mo ago
Yes, if your USerService takes a PAsswordEncoder as a parameter
Koblížkáč
KoblížkáčOP5mo ago
yup
tjoener
tjoener5mo ago
Then you declare
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
in a Configuration class somewhere
Koblížkáč
KoblížkáčOP5mo ago
yes i already changed that youre true it wouldnt make sense to return a concrete class and interface is more suitable for mocking lol
tjoener
tjoener5mo ago
Test works now?
Koblížkáč
KoblížkáčOP5mo ago
the userWrong for some reason gets saved
No description
tjoener
tjoener5mo ago
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> ...);
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> ...);
I like doing it this way btw But up to you ah yes, because who checks the validity?
Koblížkáč
KoblížkáčOP5mo ago
im pretty sure annotating the component with @Validated and @Valid on method parameter is enough to check the validation automatically atleast i read it on docs somewhere for sure
tjoener
tjoener5mo ago
You shouldn't validate on the service btw That's the controller's job
tjoener
tjoener5mo ago
yeah, you shouldn't do that in general? Unless this is like a new thing a lot of people do But Controllers handle the IO, they convert your web request from json or whatever to an object, that get's validated and passed to your service So put a @Valid on the @RequestBody in the controller
Koblížkáč
KoblížkáčOP5mo ago
ah okay so i shouldnt validate in services good to know
tjoener
tjoener5mo ago
no
Koblížkáč
KoblížkáčOP5mo ago
okay i removed the validation but now the saving fails
tjoener
tjoener5mo ago
aha
Koblížkáč
KoblížkáčOP5mo ago
No description
No description
tjoener
tjoener5mo ago
with? means your save method doesn't return the value Because the repository save method doesn't have an implementation in your mock you can have it return the first parameter with thenAnswer for example, or just verify that you call the save method I would also verify the passwordEncoder method is called
Koblížkáč
KoblížkáčOP5mo ago
it is but returns null... therefore i will need to mock it
tjoener
tjoener5mo ago
indeed
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
sorry i already solved it but i kinda have a dilema over these 2 things, which should i choose:
No description
No description
No description
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
because maybe i want to pass some default permission to the user?
Koblížkáč
KoblížkáčOP5mo ago
@tjoener im tryint to test this, but its not working again, any idea why is that so?
No description
No description
No description
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
?
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
no
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
i dont want to spend like 10 hours watching guides on how to create tests bruh
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
thats true
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
it makes sense though in my head, i mock whats supposed to be mocked, and then i test the service layer
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
i mock repository and pwd encoder
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
im leaving for a wekk tommorow, so i might check out some guides while bored i guess @Agit Rubar Demir any guide you would recommend?
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
Koblížkáč
KoblížkáčOP5mo ago
well during that time i wont have laptop with me i meant rather like watch only things for like theory or smth so atleast i know what im writing
Unknown User
Unknown User5mo ago
Message Not Public
Sign In & Join Server To View
tjoener
tjoener5mo ago
Wait who's helping now Always try to return immutable collections (Set.of in this case). Having immutable types makes it easier to reason about your program
JavaBot
JavaBot5mo ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
Want results from more Discord servers?
Add your server