지난주에 이어 이번주에도 TDD와 테스트 코드에 대해 공부한다.
1주차: TDD와 테스트 코드(1)
테스트 코드? 단위 테스트 통합 테스트 좋은 테스트의 특징 TDD? TDD를 사용해야 하는 이유 TDD 작성 방법 TDD의 장점 1️⃣ 테스트 코드? : 소프트웨어의 기능과 동작을 테스트하는데 사용되는 코드
fluttering-girdle-e7f.tistory.com
TDD와 테스트 코드에 대한 이론적인 내용은 해당 글을 참고해주세요 :)
JUnit?
Mockito?
1. Mock 객체를 생성하는 방법
2. Mock이 어떻게 동작해야 하는지 관리하는 방법
3. Mock의 행동을 검증하는 방법
테스트 어노테이션
테스트 클래스와 메소드
테스트 코드 구현
1️⃣ JUnit?
: 자바 프로젝트를 위한 단위 테스트 프레임워크
🌍 JUnit5의 규칙
JUnit5에서 테스트를 실행하기 위해서는 다음의 한 가지만 지켜주면 된다.
➡ 테스트 메소드는 @Test 어노테이션이 붙어있어야 함!
💡 JUnit5?
이전 JUnit 버전과 다르게, JUnit5는 세개의 서브 프로젝트로 이루어져 있다.
JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit5는 크게 3가지 모듈로 구성된다.
- JUnit Platform : JVM에서 테스트 프레임워크를 실행하는데 기초를 제공한다. 또한 테스트를 발견하고 테스트 계획을 생성하는 TestEngine API를 제공해 테스트 프레임워크를 개발할 수 있다.
- JUnit Jupiter : JUnit5에 새로 추가된 테스트 코드용 API 로, JUnit Jupiter는 JUnit 5에서 테스트를 작성하고 확장을 하기 위한 새로운 프로그래밍 모델과 확장 모델의 조합이다. Jupiter 는 테스트 코드 작성에 필요한 junit-jupiter-api 모듈과 테스트 실행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.
- JUnit Jupiter = junit-jupiter-api + junit-jupiter-engine
- JUnit Vintage : Unit Vintage는 하위 호환성을 위해 JUnit3과 JUnit4를 기반으로 돌아가는 플랫폼에 테스트 엔진을 제공해준다.
2️⃣ Mockito?
: Mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있는 방법을 제공하는 Java 오픈소스 테스트 프레임워크
💡 Mock: 진짜 객체와 비슷하게 동작하지만, 프로그래머가 직접 행동을 관리하는 객체
@Mock | Mock 객체를 만들어 반환 |
@Spy | Stub하지 않은 메소드들을 원본 메소드 그대로 사용하는 어노테이션 |
@InjectMocks | @Mock, @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션 |
@MockBean | 스프링 컨텍스트에 mock객체를 등록하고 스프링 컨텍스트에 의해 @Autowired가 동작할 때 등록된 mock 객체를 사용할 수 있도록 동작하게 해준다. |
Stub : 특정 메서드 호출에 대해 미리 정의된 동작을 반환하는 객체
💡 @Mock과 @InjectMocks의 차이?
▪ @Mock와 같이 모의 객체를 생성한다는 것은 실제 객체와 동일한 메소드와 동작을 가지지만 실제 데이터나 외부 리소스와의 상호작용은 없다.
▪ @InjectMocks와 같이 모의 객체를 주입한다는 것은 테스트의 대상이 특정 모의 객체를 사용해야 할 때, 그 모의 객체를 자동으로 주입하여 테스트를 수행할 수 있도록 한다.
▪ 모의 객체를 주입하는 것은 @Mock로 생성한 모의 객체가 자동으로 주입되어 테스트가 진행된다.
✅ Mock을 활용한 테스트를 작성 시 알아야 할 내용
1. Mock 객체를 생성하는 방법
2. Mock이 어떻게 동작해야 하는지 관리하는 방법
3. Mock의 행동을 검증하는 방법
1. Mock 객체를 생성하는 방법
모조 객체를 만들어 사용하고 싶은 클래스를 @Mock 어노테이션 혹은 @MockBean 어노테이션을 사용하여 필드에 주입한다. (어노테이션을 사용하려면 @ExtendWidth를 사용해야 한다)
💡 @Mock과 @MockBean의 차이?
둘 다 모조 객체를 주입한다는 점에서는 동일하나 @MockBean은 모조 객체를 Bean으로써 관리할 수 있도록 만들어준다.
2. Mock이 어떻게 동작해야 하는지 관리하는 방법 : Stub
모의 객체의 메서드 호출에 대한 ‘예상 동작’을 정의한다.
3. Mock의 행동을 검증하는 방법 : Verify
모의 객체에 대해 특정 메서드가 호출되고 예상된 인자와 함께 호출되었는지를 검증하는 메소드를 제공한다.
3️⃣ 테스트 어노테이션
@DisplayName | 테스트 클래스나 테스트 메소드에 이름을 붙여줄때 사용 |
@DisplayNameGeneration | 클래스에 해당 어노테이션을 붙이면 @Test 메소드 이름에 언더바(_)로 표시한 모든 부분은 공백으로 처리된다. |
@BeforeEach | 각각 테스트 메소드가 실행되기전에 실행되어야 하는 메소드를 명시해준다. @Test , @RepeatedTest , @ParameterizedTest , @TestFactory 가 붙은 테스트 메소드가 실행하기 전에 실행된다. |
@AfterEach | @Test , @RepeatedTest , @ParameterizedTest , @TestFactory 가 붙은 테스트 메소드가 실행되고 난 후 실행된다. |
@BeforeAll | 테스트가 시작하기 전 딱 한 번만 실행 된다. |
@AfterAll | 테스트가 완전히 끝난 후 딱 한 번만 실행 된다. |
@Nested | 테스트 클래스안에 Nested 테스트 클래스를 작성할 때 사용되며, static이 아닌 중첩된 클래스, 즉 inner 클래스여야만 한다. |
@Tag | 테스트를 필터링할 때 사용한다. 클래스또는 메소드레벨에 사용한다. |
@Disabled | 테스트 클래스나, 메소드의 테스트를 비활성화 한다. |
@Timeout | 주어진 시간안에 테스트가 끝나지 않으면 실패한다. |
@ExtendWith | extension을 등록한다. |
@RegisterExtension | 필드를 통해 extension을 등록한다. |
@TempDir | 필드 주입이나 파라미터 주입을 통해 임시적인 디렉토리를 제공할 때 사용한다 |
💡 @BeforeEach VS @BeforeAll
@BeforeEach는 각 테스트 메소드마다 실행되지만 @BeforeAll는 테스트가 시작하기 전 딱 한 번만 실행 된다.
@AfterEach과 @AfterAll의 차이도 동일하다.
4️⃣ 테스트 클래스와 메소드
(1) 테스트 클래스 : 적어도 한개의 @Test 어노테이션이 달린 테스트 메소드가 포함되어있는 클래스를 말한다.
(2) 테스트 메소드 : @Test ,@RepeatedTest ,@ParamterizedTest,@TestFactory ,@TestTemplate 같은 메타 어노테이션이 붙여진 메소드를 말한다.
(3) 라이프사이클 메소드 : @BeforeAll , @AfterAll , @BeforeEach , @AfterEach 같은 메타 어노테이션이 붙여진 메소드를 말한다.
5️⃣ 테스트 코드 구현
사용자 조회, 추가, 삭제 메소드를 구현하여 테스트를 진행했다.
User
@Getter
@Setter
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String name;
}
사용자 테이블은 간단하게 id, email, name으로 구성했다.
UserService
@Service
@AllArgsConstructor
public class UserService {
private UserRepository userRepository;
// 사용자 추가 메소드
public User registerUser(User user) {
return userRepository.save(user);
}
// 사용자 조회 메소드
public User getUserById(Long id) {
Optional<User> user = userRepository.findById(id);
return user.orElse(null);
}
// 사용자 삭제 메소드
public void deleteUser(User user) { userRepository.delete(user); }
}
사용자 추가, 조회, 삭제 메소드를 각각 구현했다.
UserServiceTest
1. @ExtendWith(MockitoExtension.class)
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock을 사용하기 위해 @ExtendWith(MockitoExtension.class)를 사용했다.
2. @Mock과 @InjectMocks
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
private User testUser;
@BeforeEach
public void setUp() {
testUser = new User();
testUser.setId(1L);
testUser.setName("user");
testUser.setEmail("user01@example.com");
}
테스트 클래스에서
(1) 실제 객체와 동일한 메소드와 동작을 가지지만 실제 데이터나 외부 리소스와의 상호작용은 없는 UserRepository에는 @Mock을
(2) 테스트가 특정 모의 객체를 사용해야 할 때, 모의 객체를 자동으로 주입하여 테스트를 수행할 수 있도록 하기 위해 UserService에 @InjectMocks을 사용했다.
@BeforeEach를 사용해 테스트 메소드 실행 전에 매번 User를 설정해준다.
3. 테스트 메소드
@DisplayName("사용자 추가 테스트")
@Test
public void testCreateUser() {
// Given
when(userRepository.save(testUser)).thenReturn(testUser);
// When
User addedUser = userService.registerUser(testUser);
// Then
assertEquals(testUser.getName(), addedUser.getName());
assertEquals(testUser.getEmail(), addedUser.getEmail());
}
@DisplayName("사용자 조회 테스트")
@Test
public void testGetUserById() {
// Given
when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));
// When
User retrievedUser = userService.getUserById(1L);
// Then
assertEquals(testUser.getId(), retrievedUser.getId());
assertEquals(testUser.getName(), retrievedUser.getName());
assertEquals(testUser.getEmail(), retrievedUser.getEmail());
}
@DisplayName("사용자 삭제 테스트")
@Test
public void testDeleteUser() {
// Given
// When
userService.deleteUser(testUser);
// Then
verify(userRepository, times(1)).delete(testUser);
}
@Test 어노테이션으로 지정한 테스트 메소드들을 실행한다. 각 코드는 given-when-then 패턴에 맞춰 작성했다.
4. 테스트 실행
테스트를 실행하면 @DisplayName으로 설정한대로 보이는 것을 알 수 있다.
참고
JUnit5 완벽 가이드
시작하기전
donghyeon.dev
[Java] JUnit의 진화 과정과 public 접근 제어자
아래의 내용은 토비님과 토비의 스프링 읽기를 하면서 얘기가 나온 부분을 개인적으로 공부한 후에 정리한 것입니다. 1. JUnit의 진화 과정과 public 접근 제어자 Java 진영에서 사용되는 테스트 프
mangkyu.tistory.com
[Java] Spring Boot Mockito 이해하기 : 테스트 흐름 및 사용예시
해당 글에서는 Mockito에 대해 이해하고 활용하는 방법에 대해 확인해 봅니다. 💡 [참고] JUnit5에 기능들과 사용예시에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다. [Java] Spring Boot JUnit
adjh54.tistory.com
'Study > Server 심화' 카테고리의 다른 글
6주차: 동시성 처리(2) (0) | 2024.05.28 |
---|---|
5주차: 동시성 처리(1) (1) | 2024.05.21 |
4주차: 스프링 시큐리티 + JWT(2) (1) | 2024.05.14 |
3주차: 스프링 시큐리티 + JWT(1) (0) | 2024.05.07 |
1주차: TDD와 테스트 코드(1) (2) | 2024.04.02 |