Home > Net >  Value For Variable Not Overwritten When Using Mockito
Value For Variable Not Overwritten When Using Mockito

Time:11-21

Apologies if I may be misunderstanding how Mockito works, but I'm trying to test for the case when one of my properties is null, setting it through mockito but it won't overwrite that unless I set the value for the whole object as null or make into a method and mock that instead and with the use of a spy.

Please let me try to better explain this with my code.

This is my class and the piece I want to test for. I'm checking the case where something wrong happens on saving a follower to a user so that is not set and returned empty. To be honest, I'm also not sure if it makes sense to test this with an if or whether I should be catching some exception here. But back to the original problem, the followerEntity object is always being created with the params for userTo and userFrom and this is what I'm trying to have mockito overwrite as null on the save call but in a way that it would return the other values for the user object. I'm not too sure, however, where I may be getting this wrong.


    @Mock
    UserRepository userRepository;

    @Spy
    @InjectMocks
    UserServiceImpl userService;

    @Override
    public void followUser(UUID fromId, UUID toId) throws FollowerNotFoundException {

        Optional<UserEntity> userEntityOptionalFrom = userRepository.findById(fromId);
        Optional<UserEntity> userEntityOptionalTo = userRepository.findById(toId);

        if (userEntityOptionalFrom.isEmpty() || userEntityOptionalTo.isEmpty()) {
            throw new UserNotFoundException("No user found with this id");
        }

        UserEntity userEntityTo = userEntityOptionalTo.get();
        UserEntity userEntityFrom = userEntityOptionalFrom.get();

        Set<FollowingRequestEntity> followingRequestEntities = new HashSet<>();
        FollowingRequestEntity followingRequestEntity = FollowingRequestEntity.builder().userSenderEntity(userEntityFrom).userReceiverEntity(userEntityTo).build();
        followingRequestEntities.add(followingRequestEntity);

        userEntityTo.setFollowedByEntity(followingRequestEntities);

        userEntityTo = userRepository.save(userEntityTo);
        
        
        if (userEntityTo.getFollowedByEntity() == null || userEntityTo.getFollowedByEntity().isEmpty()) {
            throw new FollowerNotFoundException("Follower Not Found");
        }
    }

   public UserEntity setFollower(UserEntity userEntityTo, UserEntity userEntityFrom) { // The tests work if calling and mocking this instead
        Set<FollowingRequestEntity> followingRequestEntities = new HashSet<>();
        FollowingRequestEntity followingRequestEntity = FollowingRequestEntity.builder().userSenderEntity(userEntityFrom).userReceiverEntity(userEntityTo).build();
        followingRequestEntities.add(followingRequestEntity);

        userEntityTo.setFollowedByEntity(followingRequestEntities);
        
        return userEntityTo;
    }

This is what I have for my test. As you can see, I've tried both when..Return() and doReturn() and also forcing the follower entity values to null, but when debugging my user object it always shows the FollowerBy property populated and not null.

    @Test
    void testFollowUser_ThrowsExceptionWhenFollowerIsFound() {

        UUID userFromId = randomUUID();
        UUID userToId = randomUUID();
        UserEntity userEntityFrom = getUserEntity();
        userEntityFrom.setId(userFromId);

        UserEntity userEntityTo = getUserEntity();
        userEntityTo.setId(userToId);
        userEntityTo.setName("new name");
       
      
when(userRepository.findById(userEntityFrom.getId())).thenReturn(Optional.of(userEntityFrom));
 when(userRepository.findById(userEntityTo.getId())).thenReturn(Optional.of(userEntityTo);


//        when(userRepository.save(userEntityTo)).thenReturn(userEntityTo);
        userEntityTo.setFollowerOfEntity(null);
        userEntityTo.setFollowedByEntity(null);
        doReturn(userEntityTo).when(userRepository).save(any());
//        when(userEntityTo.getFollowedByEntity()).thenReturn(null);
        
//        doReturn(userEntityTo).when(userService).setFollower(any(), any());
        
        FollowerNotFoundException exception =
                assertThrows(FollowerNotFoundException.class, () -> userService.followUser(userFromId, userToId));

        assertEquals("Follower Not Found", exception.getMessage());
    }

Thank you very much.

CodePudding user response:

Here's a complete example (compressed into a single file) which explains what I have been trying to get across in the comments above:

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

class MyService {
    private final MyRepository myRepository;

    MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }

    public String doWork() {
        return myRepository.get().toUpperCase();
    }
}

class MyRepository {
    public String get() {
        return "a real thing from the DB";
    }
}

@ExtendWith(MockitoExtension.class)
public class MyTests {
    @Mock
    MyRepository myRepository;

    @InjectMocks
    MyService myService;

    @Test
    public void aTest() {
        when(myRepository.get()).thenReturn("something fake");
        assertEquals("SOMETHING FAKE", myService.doWork());
    }
}

The actual non-test code (which will live in src/main/java) is MyService and MyRepository. These classes have no reference to any test code.

MyTests is in src/test/java. It's the only part of your code which knows about mocks.

We can remove some of the "magic" by explicitly creating and injecting the mocks:

...
    MyRepository myRepository;

    MyService myService;

    @BeforeEach
    public void setup() {
        myRepository = mock(MyRepository.class);
        myService = new MyService(myRepository);
    }
...

Looking at the function you are testing we see:

        followingRequestEntities.add(followingRequestEntity);

        userEntityTo.setFollowedByEntity(followingRequestEntities);

        userEntityTo = userRepository.save(userEntityTo);
        
        
        if (userEntityTo.getFollowedByEntity() == null || userEntityTo.getFollowedByEntity().isEmpty()) {
            throw new FollowerNotFoundException("Follower Not Found");
        }

So as followingRequestEntities always contains one entry, userEntityTo.getFollowedByEntity().isEmpty() will always be false, no matter what your mocks return, and the exception can never be thrown.

You can use:

when(userRepository.findById(userEntityFrom.getId())).thenReturn(Optional.empty());

and expect that UserNotFoundException will be thrown.

  • Related