[Spring] Mockito를 사용해 Spring Service 테스트하기
Mockito를 사용해 Spring Service를 테스트 해보자
환경
- Java
- Spring
- JUnit5
- SpringExtension.class
Mockito를 통한 단위 테스트
Mockito
Mockito
: Java에서 모킹(Mocking)을 지원해주는 테스트 프레임워크@Mock
: 테스트에 필요한 객체를 모킹(Mocking)해주는 어노테이션@InjectMocks
: 테스트에 필요한 객체를 모킹(Mocking)해주는것과 함께@Mock
으로 모킹된 객체들 중에서 필요한 객체들을 주입해주는 어노테이션when(method_name).thenReturn(return_value);
: 모킹(Mocking)된 객체의 특정 메소드(method_name
) 호출시return_value
를 반환하도록 설정 가능한 메소드
예제
build.gradle
- 아래 build.gradle은 개인 프로젝트 환경으로 다를 수 있으며 자세한 설정은 아래 링크들을 참고
- https://www.baeldung.com/junit-5
- https://www.baeldung.com/junit-5-gradle
- https://www.baeldung.com/spring-boot-testing
plugins {
id 'org.springframework.boot' version '2.5.13'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'org.twpower'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-web')
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation('org.junit.jupiter:junit-jupiter-api')
testImplementation('org.mockito:mockito-core')
testImplementation('org.mockito:mockito-junit-jupiter')
testImplementation('org.hamcrest:hamcrest')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine')
}
test {
useJUnitPlatform()
}
Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")})
public class UserEntity {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String email;
@Column(nullable = false)
private String password;
}
JPA Repository
@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
UserEntity findByEmail(String email);
Boolean existsByEmail(String email);
}
Service
@Slf4j
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public UserEntity create(final UserEntity userEntity){
if (userEntity == null || userEntity.getEmail() == null) {
throw new RuntimeException("Invalid Arguments");
}
final String email = userEntity.getEmail();
if (userRepository.existsByEmail(email)) {
log.warn("Email already exists {}", email);
throw new RuntimeException("Email already exists");
}
return userRepository.save(userEntity);
}
}
Test
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void createTest() {
Assertions.assertThrows(RuntimeException.class, () -> {
userService.create(null);
});
UserEntity userEntityForNullEmail = UserEntity.builder().build();
Assertions.assertThrows(RuntimeException.class, () -> {
userService.create(userEntityForNullEmail);
});
UserEntity userEntityForExistUser = UserEntity.builder()
.email("test@test.com")
.username("testUsername")
.password("testPassword")
.build();
when(userRepository.existsByEmail(any())).thenReturn(true);
Assertions.assertThrows(RuntimeException.class, () -> {
userService.create(userEntityForExistUser);
});
UserEntity userEntity = UserEntity.builder()
.email("test@test.com")
.username("testUsername")
.password("testPassword")
.build();
reset(userRepository);
when(userRepository.save(any())).thenReturn(userEntity);
UserEntity returnedUserEntity = userService.create(userEntity);
Assertions.assertEquals("test@test.com", returnedUserEntity.getEmail());
Assertions.assertEquals("testUsername", returnedUserEntity.getUsername());
Assertions.assertEquals("testPassword", returnedUserEntity.getPassword());
}
}