To test this code:
@Service
public class StudentService { 
  @Override
  public void removeStudentByID(Long studentId) throws StudentExistsException 
    boolean exists = studentRepo.existsById(studentId);
    if (!exists) {
        throw new StudentExistsException("Student doesn't exist");
    }
    studentRepo.deleteById(studentId);
  }
}
We can mock/"unit test" like this:
package com.example.demo;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class StudenServiceTests { // we only test StudentService, since we (sort of) trust studentRepository (tested it elsewehere/how)
  @Mock // A Mock! (alternatively @MockBean)
  private StudentRepository studentRepoMock;
  @InjectMocks // A real service (with mocks injected;)!!! (alternatively @Autowired/...)
  private StudentService testee; // this guy we want to test (to the bone!)
  @Test // ..the "good case"
  void removeStudentByIdExists() throws StudentExistsException {
    // Given:
    // we don't even need a student, only his id
    // instruct mock for the "first interaction" (see testee code):
    when(studentRepoMock.existsById(1L)).thenReturn(true);
    // instruct mock for "second interaction" (see testee code):
    doNothing().when(studentRepoMock).deleteById(1L); // we could also use ...delete(any(Long.class))  or ..anyLong();
    // When: (do it!)
    testee.removeStudentByID(1L);
    // Then: Verify (interactions, with as exact as possible parameters ..Mockito.any***)
    verify(studentRepoMock).existsById(1L); //.times(n) ...when you had a list ;)
    verify(studentRepoMock).deleteById(1L);
  }
  @Test // .. the "bad (but correct) case"/exception
  void removeStudentByIdNotExists() throws StudentExistsException {
    // Given:
    when(studentRepoMock.existsById(1L)).thenReturn(false);
    // When:
    StudentExistsException thrown = assertThrows(
            StudentExistsException.class,
            () -> testee.removeStudentByID(1L),
            "Expected testee.removeStudentByID to throw, but it didn't");
    // Then:
    assertNotNull(thrown);
    assertTrue(thrown.getMessage().contains("Student doesn't exist"));
  }
}
... run with mvn test.
With these two test cases, we achieve 100% test (& branch) coverage for the given method/service.
But don't mix it up with "integration test" (where we use a real repo/data base), then we can use:
An sample on "default" (assuming empty) database, with preparation (and @Rollback):
package com.example.demo;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
@SpringBootTest
class StudentServiceIT { // .. here we try to test "all of our application" (as good as possible ..to the bone)
  @Autowired //! real "bean", no mock... this is only auxilary for this test, we could use some other method to prepare & verify
  private StudentRepository studentRepository;
  @Autowired //!! "real service"
  private StudentService studentService;
  @Test
  @Rollback // actually our test is "self-cleaning"...when no exceptions ;)
  void removeStudentByIdExists() throws StudentExistsException {
    // Given:
    Student student = new Student(1L, "Drake", "drake@gmail.com");
    studentRepository.save(student);
    // When:
    studentService.removeStudentByID(1L);
    // Then:
    assertFalse(studentRepository.findById(1L).isPresent());
  }
  @Test
  @Rollback // in any case (if assumptions were wrong;)
  void removeStudentByIdNotExists() throws StudentExistsException {
    // Given:
    // nothing, we assume "empty db"
    // When:
    StudentExistsException thrown = assertThrows(
            StudentExistsException.class,
            () -> studentService.removeStudentByID(1L),
            "Expected testee.removeStudentByID to throw, but it didn't");
    // Then:
    assertNotNull(thrown);
    assertTrue(thrown.getMessage().contains("Student doesn't exist"));
  }
}
(Run with: mvn failsafe:integration-test)
Thanks to/Refs:
Complete Sample@github