Mockito testing void method which throws exception. Spring Boot, eclipse IDE

Viewed 1016

How to test a void method which throws an exception?

I've read many publications here and on others pages including Mockico docs form https://javadoc.io/static/org.mockito/mockito-core/3.12.4/org/mockito/Mockito.html#5. And my test still won't pass. I don't know what I'm doing wrong I always get red.

Maybe it is related to a wrongly implemented exception class.

Here is my code:

Repo:

import org.springframework.data.jpa.repository.JpaRepository;

import succeed.app.start.model.User;

public interface UserRepository extends JpaRepository<User, Long> {

}

Service interface:

public interface UserService {

    void deleteUserById(long userId);
}

Service implementation:

@Service
public class UserServiceImpl implements UserService {

    private UserRepository userRepository;
    
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public void deleteUserById(long userId) {
        userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User", "ID", userId));
        
        userRepository.deleteById(userId);
    }
}

Exception class:

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    
    private final static long serialVersionUID = 1L;
    private String resourceName;
    private String fieldName;
    private Object fieldValue;
    
    public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {
        super(String.format("%s not found with %s : %s", resourceName, fieldName, fieldValue));
        this.resourceName = resourceName;
        this.fieldName = fieldName;
        this.fieldValue = fieldValue;
    }
    
    public String getResourceName() {
        return resourceName;
    }
    
    public String getFieldName() {
        return fieldName;
    }
    
    public Object getFieldValue() {
        return fieldValue;
    }
    
}

Test class:

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

import static org.mockito.Mockito.doThrow;

import succeed.app.start.exception.ResourceNotFoundException;
import succeed.app.start.repository.UserRepository;

@ExtendWith(MockitoExtension.class)
public class UserServiceImplTest {
    
    @Mock
    UserRepository userRepository;
    
    @Mock
    UserServiceImpl userService;
    
    
    @Test
    @DisplayName("Should throw ResourceNotFoundException when user doesn't exist.")
    void shouldThrowsResourceNotFoundException() {
        
        final long nonExistingId = 12902450235L;
        
        doThrow(new ResourceNotFoundException("User", "ID", nonExistingId)).when(userService).deleteUserById(nonExistingId);
        
        userService.deleteUserById(nonExistingId);
        
    }
    
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>succeed.app</groupId>
    <artifactId>start</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>start</name>
    <description>App to organize and achieve goals.</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
1 Answers

There are a couple of things wrong here.

First, other than the message in the @Display annotation, your test doesn't really expect the exception to be thrown. You need to explicitly code this behavior, e.g. by using assertThrows.

Second, you're mocking the UserService, so the test won't really do anything - in the same method, you've defined some fake behavior and then tested it. Instead, you should mock the UserRepository and then make test that the UserService calls it correctly:

@ExtendWith(MockitoExtension.class)
public class UserServiceImplTest {
    @Mock
    UserRepository userRepository;

    @InjectMocks
    UserServiceImpl userService;

    @Test
    @DisplayName("Should throw ResourceNotFoundException when user doesn't exist.")
    void shouldThrowsResourceNotFoundException() {
        final long nonExistingId = 12902450235L;

        doReturn(Optional.empty()).when(userRepository).findById(nonExistingId);
        assertThrows(
            ResourceNotFoundException.class,
            () -> userService.deleteUserById(nonExistingId));
    }
}
Related