#Mocking fails for some reason

141 messages · Page 1 of 1 (latest)

sterile kite
#
@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",
            "john.doe@example.com",
            "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?

amber pelicanBOT
#

This post has been reserved for your question.

Hey @sterile kite! 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.

brave heron
#

You don't need to isNotEmpty, the hasValue... already checks that

#

Show the UserService

#

Also this should definitely not be a SpringBootTest

amber pelicanBOT
#

💤 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.

sterile kite
#

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);
    }
}
brave heron
#

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

sterile kite
#

well, thats just a little thing, im more worried about why the test isnt passing

brave heron
#

Remove all those annotations from the test

#

And just run with @ExtendWith(MockitoExtension.class)

#

From the test class

sterile kite
brave heron
#

That doesn't make sense. This is how I always do it

brave heron
sterile kite
#
@ExtendWith(MockitoExtension.class)
class UserServiceTests {

    User testUser = new User(
            1L,
            "xxxJohnxxx",
            "John Doe",
            "john.doe@example.com",
            "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("john.doe2@example.com").password("Password1").build();
        assertThatThrownBy(() -> userService.save(userWrong))
                .isInstanceOf(ConstraintViolationException.class);
        assertThat(userService.save(userCorrect)).isNotNull();
    }
}
brave heron
#

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

#

Instead of SpringBootTest

#

Because that will start your entire spring application basically, for no good reason

sterile kite
#

hmm yes that really makes sense, its now almost working as expected, but its throwing NPE because passwordencoder is null for some reason

brave heron
#

yes, you didn't mock it...

sterile kite
#

so now its loaded only what is mocked?

#

if i understand correctly

brave heron
#

yes

sterile kite
#

so i suppose i should make separate configuration and load it

brave heron
#

no

#

UNIT test

#

Just mock it

#

That's also why I said use PasswordEncoder

#

Because that's an interface

sterile kite
#

right, but how do i mock it if i dont know what will it would return?

brave heron
#

It doesn't matter for your test what it returns

sterile kite
#

so like when(passwordEncoder.encode("Password1")).thenReturn("hashedPassword"); is good enough?

brave heron
#

What are you doing when you do that?

#

You don't need hashedPAssword do you?

sterile kite
#

i placed it into the saving test

brave heron
#

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

sterile kite
#

thats why i like kotlin

brave heron
#

You made a builder

sterile kite
#

yes

brave heron
#

Just don't make a builder...

#

Constructors exist

#

You're using builders as a vague way to get named parameters

sterile kite
brave heron
#

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

sterile kite
#

so adding

@Mock
PasswordEncoder passwordEncoder;

is enough?

brave heron
#

Yes, if your USerService takes a PAsswordEncoder as a parameter

sterile kite
#

yup

brave heron
#

Then you declare java @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } in a Configuration class somewhere

sterile kite
#

yes i already changed that

#

youre true it wouldnt make sense to return a concrete class and interface is more suitable for mocking lol

brave heron
#

Test works now?

sterile kite
#

the userWrong for some reason gets saved

brave heron
#
assertThatExceptionOfType(ConstraintViolationException.class)
  .isThrownBy(() -> ...);```
#

I like doing it this way btw

#

But up to you

#

ah yes, because who checks the validity?

sterile kite
#

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

brave heron
#

You shouldn't validate on the service btw

#

That's the controller's job

sterile kite
brave heron
#

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

sterile kite
#

ah okay

#

so i shouldnt validate in services

#

good to know

brave heron
#

no

sterile kite
#

okay i removed the validation

#

but now the saving fails

brave heron
#

aha

sterile kite
brave heron
#

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

sterile kite
#

it is but returns null...

#

therefore i will need to mock it

brave heron
#

indeed

undone wigeon
#

@sterile kite I can help you

#

If you can share your screen, we can look together

sterile kite
undone wigeon
sterile kite
#

@brave heron im tryint to test this, but its not working again, any idea why is that so?

undone wigeon
undone wigeon
#

permissions = new ArrayList<>();

#

Then you can create add(Permission permission) method in these class

undone wigeon
#

Do you have any test course?

sterile kite
undone wigeon
sterile kite
#

i dont want to spend like 10 hours watching guides on how to create tests bruh

undone wigeon
#

Because your screenshots show me you dont understand unit test

sterile kite
#

thats true

undone wigeon
sterile kite
#

it makes sense though in my head, i mock whats supposed to be mocked, and then i test the service layer

sterile kite
undone wigeon
#

If you mocked anything you should verify these method

#

If you don’t mocked anything your test is not unit test

sterile kite
#

im leaving for a wekk tommorow, so i might check out some guides while bored i guess

#

@undone wigeon any guide you would recommend?

undone wigeon
sterile kite
#

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

undone wigeon
#

👌🏼

brave heron
#

Wait who's helping now

brave heron