I would like to test my delete method which looks like:
public void deleteUser(String id) {
var userEntity = userRepository.findById(Integer.valueOf(id))
.orElseThrow(() -> new UserNotFoundException("Id not found"));
if (userEntity.getLastAccessDate() == null) {
throw new ProhibitedAccessException("Policy has been violated");
}
userRepository.delete(userEntity);
}
My delete method in repository is the following:
#Modifying
#Query("update UserEntity u set deleted = true where u = :userEntity")
void delete(UserEntity userEntity);
And I've written the following test:
#Test
void deleteUserTest(){
final int id = 1;
UserEntity userEntity = new UserEntity();
var idString = String.valueOf(id);
when(userRepository.findById(id)).thenReturn(Optional.of(userEntity));
assertThrows(RuntimeException.class, () -> userService.deleteUser(idString));
}
This test is working good but it didn't cover the
userRepository.delete(userEntity);
Could you help me please - how can I add it to my test? Previously, I've tried to to do it through verify but it didn't help.
Test coverage means, which lines of your code are being called. If you mock an object, you are not calling the real code but only simulate the behaviour
Your only test the implementation of your userService and mock the behaviour of your userRepository.
So your test only covers the code inside of your userService.deleteUser(...) method, but not the code inside of your userRepository.
If you want to cover your userRepository, you have to write a test with a 'real' userRepository.
Related
Hi have read a lot about this but can't come to a conclusion about the best way to test a method that is dependent on other method call results to perform its actions.
Some of the questions I've read include:
Testing methods that depend on each other
Unit testing a method that calls other methods
Unit testing a method that calls another method
Some of the answers sugest that we should only test the methods that perform only one action and then test the method that call this methods for conditional behaviuour (for example, verifying if a given method was called or not) and that's fine, I get it, but I'm struggling with other scenario.
I have a service with a REST api.
The controller has a create method that receives a DTO and calls the Service class create method with this argument (DTO).
I'm trying to practice TDD and for this I use this project I'm building without a database.
The code is as follows:
#Service
public class EntityService implements FilteringInterface {
private MemoryDatabase db = MemoryDatabase.getInstance();
//Create method called from controller: receives DTO to create a new
//Entity after validating that it's name and code parameters are unique
public EntityDTO create(EntityDTO dto) throws Exception {
validateUniqueFields(dto);
Entity entity = Entity.toEntity(dto, "id1"); //maps DTO to Entity object
db.add(entity);
return new EntityDTO.Builder(entity);//maps entity to DTO
}
public void validateUniqueFields(EntityDTO dto) throws Exception {
Set<Entity> foundEntities = filterEntityByNameOrCode(dto.getName(),
dto.getCode(), db.getEntities());
if (!foundEntities.isEmpty()) {
throw new Exception("Already exists");
}
}
}
This is the interface with methods reused by other service classes:
public interface FilteringInterface {
default Set<Entity> filterEntityByNameOrCode(String name, String code, Set<Entity> list) {
return list.stream().filter(e -> e.getSiteId().equals(siteId)
&& (e.getName().equals(name)
|| e.getCode().equals(code))).collect(Collectors.toSet());
}
default Optional<Entity> filterEntityById(String id, Set<Entity> list) {
return list.stream().filter(e -> e.getId().equals(id)).findAny();
};
}
So, I'm testing this service class and I need to test the create() method because it can have different behaviors:
If the received DTO has a name that already exists on the list of entities -> throws Exception
If the received DTO has a code that already exists on the list of entities -> throws Exception
If the received DTO has a name and a code that already exists on the list of entities -> throws Exception
If name and code are different, than everything is ok, and creates the entity -> adds the entity to the existing list - > converts the entity to DTO and retrieves it.
Problem:
To test any of the scenarios, suppose, scenario 1: I need to make the filterEntityByNameOrCode() method return a list with an Entity that has the same name as the Entity I'm trying to create. This method is called inside validateUniqueFields() method.
Problem is: I can't call mockito when() for any of this methods because, for that, I would have to mock the service class, which is the class that I'm testing and, thus, it's wrong approach.
I've also read that using Spy for this is also wrong approach.
So, where thus that leaves me?
Also: if this code is not the correct aprocah, and thats why
it can't be correctly tested, than, whats should the correct approach be?
This service will have other methods (delete, update, etc.). All of this methods will make use of the FilteringInterface as well, so I will have the same problems.
What is the correct way of testing a service class?
I would apply an DI pattern in your service, in order to mock and control the db variable.
#Service
public class EntityService implements FilteringInterface {
private Persistence db;
public EntityService(Persistence db) {
this.db = db;
}
}
After that, you will be able to add entities to Set accordingly to your scenarios
#ExtendWith(MockitoExtension.class)
class EntityServiceTest {
#Mock
private Persistence persistence;
#InjectMocks
private EntityService entityService;
#BeforeEach
void before() {
final Set<Entity> existentEntity = Set.of(new Entity(1L,1L, "name", "code"));
when(persistence.getEntities()).thenReturn(existentEntity);
}
#Test
void shouldThrowWhenNameAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "name", "anything");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldThrowWhenCodeAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "anything", "code");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldThrowWhenNameAndCodeAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "name", "code");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldNotThrowWhenUnique() {
final EntityDTO dto = new EntityDTO(1L, "diff", "diff");
final EntityDTO entityDTO = entityService.create(dto);
assertNotNull(entityDTO);
}
}
I am trying to test CompletableFuture.supplyAsync function with mockito but the test is not completing probably because the completable future is not returning. I am not sure what I am missing in the code. Can anyone please help.
I have written the code as follows.
So there are UserService class which returns User, UserEntityService class which returns users entities and a validation class to check if the entities belongs to the user or not.
I want to test if the passed entities belongs to user or not.
class UserService {
CompletableFuture<User> getUser(String userName) {
log.info("Fetching User with username {}", userName);
return CompletableFuture.supplyAsync(
() -> getUserByPortalUserName(userName));
}
}
class UserEntityService {
CompletableFuture<List<UserEntity>> getUserEntities(Long userId) {
log.info("Retrieving all entities for user id {}", userId);
return CompletableFuture.supplyAsync(
() -> getAllByUserId(userId));
}
}
class UserValidationService {
public boolean validateUserCounterparty(UserRequest request)
throws ExecutionException, InterruptedException {
CompletableFuture<Boolean> result = userService.getUser(request.getUserName())
.thenCompose(user -> userEntityService.getUserEntities(user.getUserId()))
.thenCompose(userEntities -> validate(userEntities, request.getUserEntities()));
Boolean validationStatus = result.get();
if (!validationStatus) {
log.error("Validation failed for user name {}", request.getUserName());
}
return validationStatus;
}
}
And the test case is written as
#ExtendWith(MockitoExtension.class)
class UserValidationServiceTest {
#Mock
UserService userService;
#Mock
UserEntityService userEntityService;
#InjectMocks
UserValidationService userValidationService;
#Before
public void init() {
MockitoAnnotations.openMocks(this);
}
#Test
public void validateUser() throws ExecutionException, InterruptedException {
CompletableFuture<User> userFuture = new CompletableFuture<>();
CompletableFuture<List<UserEntity>> userEntityFuture = new CompletableFuture<>();
Mockito.doReturn(userFuture).when(userService).getUser(anyString());
Mockito.doReturn(userEntityFuture).when(userEntityService).getUserEntities(anyLong());
UserRequest request = UserRequest.builder()
.userName("admin")
.userEntities(List.of("US", "ASIA", "EUROPE")).build();
boolean result = validationService.validateUserCounterparty(request);
assertTrue(result);
}
}
On executing this test, it goes into infinite loop and never stops. I guess its because the completable future is not returning but I dont have enough knowledge on how to prevent it.
What modification should I do to prevent it?
In your test method you're creating CompletableFuture instances using new. JavaDoc states:
public CompletableFuture()
Creates a new incomplete CompletableFuture.
So the objects you're creating are never completing, that's why the test is running infinitely. It's not actually a loop, but waiting on a blocking operation to be finished, which never happens.
What you need to do is define a CompletableFuture that completes - immediately or after some time. The simplest way of doing that is by using the static completedFuture() method:
CompletableFuture<User> userFuture =
CompletableFuture.completedFuture(new User());
CompletableFuture<List<UserEntity>> userEntityFuture =
CompletableFuture.completedFuture(List.of(new UserEntity()));
Thanks to that given objects are returned and the code can be executed fully. You can test errors in a similar way by using the failedFuture() method.
I've created a GitHub repo with a minimal reproducible example - the test presented there passes.
I am new in Junit tests and I have a question about it. Here you can see the method findById in my service class:
#Service
public class DefaultQuarterService implements QuarterService {
private final QuarterRepository quarterRepository;
public DefaultQuarterService(QuarterRepository quarterRepository) {
this.quarterRepository = quarterRepository;
}
#Override
public QuarterEntity findById(int id) {
return quarterRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("Quarter does not exist for id = %s!", id)));
}
}
And here is my QuarterRepository:
#Repository
public interface QuarterRepository extends CrudRepository<QuarterEntity, Integer> {
}
And here is my Junit implementation for this method:
#MockBean
private QuarterRepository quarterRepository;
#Test
public void throwExceptionWhenQuarterIdNotFound() {
int id = anyInt();
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
assertThatAnExceptionWasThrown(String.format("Quarter does not exist for id = %s!", id));
}
public void assertThatAnExceptionWasThrown(
String errorMsg
) {
expectException.expect(RuntimeException.class);
expectException.expectMessage(errorMsg);
}
Unfortunately test doesn't pass. Here the error in terminal:
java.lang.AssertionError: Expected test to throw (an instance of
java.lang.RuntimeException and exception with message a string
containing "Quarter does not exist for id = 0!")
Maybe it is so simple but I can not see what I am missing. I would be so happy if you can direct me. Thanks a lot!
As you mock your Repository it will return with Optional.empty() correctly, I think you should call your service's (which is Autowired) findById method. It will throw the exception actually.
First issue
In the assertThatAnExceptionWasThrown method you expect RuntimeException BUT in the service class you throw EntityNotFoundException, So I guess you should expect EntityNotFoundException in your test case.
Second issue
After this part of the code.
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
Why didn't you call your service method (findById)?
When you are returning the empty value, you should verify your condition with the service method you want to test it.
It should be something like this.
assertThatThrownBy(() -> defaultQuarterService.findById(id))
.isInstanceOf(ApiRequestException.class)
.hasMessageContaining("PUT_YOUR_EXCEPTION_MESSAGE_HERE");
This is a good sample for unit-test in the spring boot. You can check it out. Link
Try the above solutions and let me know it has been fixed or not. Good luck
I have s service methos which i want to test:
#Override
public void updateImage(long id, ImageAsStream imageAsStream) {
Product product = productRepository.findById(id)
.orElseThrow(() -> new ProductException("Product can not be found"));
updateProductImage(imageAsStream, product.getImage().getId());
}
private void updateProductImage(ImageAsStream imageAsStream, Long existingImageId) {
imageRepository.updateProductImage(existingImageId, imageAsStream);
imageRepository.copyImageToThumbnail(existingImageId);
}
So to be able to call service method, i need to mock imageRepository somehow:
#Test
void updateProductImage() {
when(imageRepository)
.updateProductImage(1L, imageAsStream).thenReturn(???);
productService.updateProductImage(1L, imageAsStream);
}
Can you please advise whats the general approach in such cases?
When I would need to test this method, then these things need to be validated:
The id is of an existing product and there is a call to the imageRepository to update the product image
The id is not of an existing product. An exception is thrown and nothing is saved in the imageRepository
For your question, it does not really matter what you return there. It can be a mock of Product, or it can be a real instance.
My preference is usually to have an Object Mother, for example ProductMother to create a "default" instance.
In code:
class ProductServiceTest {
#Test
void testHappyFlow() {
ProductRepository repository = mock(ProductRepository.class);
ProductService service = new ProductService(repository);
when(repository.findById(1L))
.thenReturn(ProductMother.createDefaultProduct());
ImageAsStream imageAsStream = mock(ImageAsStream.class);
service.updateImage(1L, imageAsStream);
verify(repository).updateProductImage(1L, imageAsStream);
verify(repository).copyImageToThumbnail(1L);
}
#Test
void testProductNotFound() {
ProductRepository repository = mock(ProductRepository.class);
ProductService service = new ProductService(repository);
assertThatExceptionOfType(ProductException.class)
.isThrownBy( () -> {
ImageAsStream imageAsStream = mock(ImageAsStream.class);
service.updateImage(1L, imageAsStream);
});
}
}
I am trying to test the method findById() method in the class below that reads data from my Database using the CrudRepository:
Class under test:
public interface PersonRepository extends CrudRepository<Person, Integer>
{
Person findById(String id);
}
Below is my test class, the test is currently passing but I would like to change it so that if the id "1" I am testing with is removed from my database, I can still run my test. I.e. do not rely on data within the database.
How can I do so?
Test Class:
public class PersonRepositoryTest {
#Mock
private PersonRepository personRepository;
#Before
public void setUp() throws Exception {
//Initialize the mocked class and ensure that it is not null
MockitoAnnotations.initMocks(this);
assertThat(personRepository, notNullValue());
}
#Test
public void testFindById() throws ParseException {
//test string
String id = "1";
//when it is called, return
when(personRepository.findById(anyString())).thenReturn(new Person());
Person person = personRepository.findById(id);
assertThat(person, notNullValue());
}
}
As mentioned in the Post comments by #Thomas, you are just mocking the database. I'm assuming you want to write a negative test case when the ID is 1.
You can just return null, instead of person Object. Instead of Matchers, pass a specific value to differentiate your positive and negative test cases.
Positive Case -
when(personRepository.findById(2)).thenReturn(new Person());
Negative Case -
when(personRepository.findById(1)).thenReturn(null);