일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #OSI #ISO #AI #서버 #자동화 #SQL #기본문법 #데이터베이스 #DBMS #Oracle #MongoDB #아키텍쳐 #DB
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #머신러닝 #딥러닝 #AI #서버 #자동화 #SQL #기본문법 #데이터베이스 #DBMS #Oracle #MongoDB #아키텍쳐 #DB
- aws #아키텍트 #과정 #s3 #bucket #생성 #이미지업로드
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #머신러닝 #딥러닝 #AI #서버 #자동화 #SQL #기본문법 #데이터베이스
- aws #아키텍트 #과정 #vpc #인프라 구축 #public subnet #route53 #igw #연결
- aws #클라우드 #퍼블릭 클라우드 #아키텍트 #과정
- aws #아키텍트 #과정 #vpc #인프라 구축 #public subnet #internet gateway #연결
- aws #아키텍트 #과정 #vpc #인프라 구축 #public subnet #igw #curl #명령어 #http
- aws #아키텍트 #과정 #vpc #인프라 구축 #private #resource #security #group
- 비트 #바이트 #이진수
- 공간복잡도 #공간자원 #캐시메모리 #SRAM #DRAM #시간복잡도
- ipv4 #private #public #instance #curl #연결성 검증 #session manager
- storage #로컬스토리지 #세션스토리지 #백그라운드 서비스
- aws #아키텍트 #과정 #vpc #인프라 구축 #퍼블릭 #프라이빗 #서브넷 #igw #route53 #routing table #ec2 #security group #firewall
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #머신러닝 #AI #서버 #자동화 #SQL #기본문법 #데이터베이스
- aws #아키텍트 #과정 #vpc #인프라 구축 #php #웹페이지 #http #public #instance
- aws #아키텍트 #과정 #vpc #인프라 구축
- aws #아키텍트 #과정 #vpc #인프라 구축 #session manager #curl #명령어 #private #instance
- 프로세스 #CPU #시공유 #커널
- mysql #linux #설정 #wordpress #웹사이트 #db 연결 #
- aws #아키텍트 #과정 #vpc #인프라 구축 #vpc #nat #gateway #private subnet
- samba #가상머신 #daemon
- 쓰레드 #쓰레드풀 #프로세스
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #머신러닝 #AI #서버 #자동화 #SQL #KDT #기본문법 #데이터베이스 #Computer #Science #CPU #메모리
- aws #아키텍트 #과정 #vpc #인프라 구축 #public subnet #private subnet
- aws #아키텍트 #과정 #vpc #인프라 구축 #private subnet #ec2 #인스턴스 #elastic compute #vm
- aws #아키텍트 #과정 #vpc #인프라 구축 #퍼블릭 #보안그룹 #생성 #http #ipv4
- aws #아키텍트 #과정 #vpc #인프라 구축 #퍼블릭 서브넷 #안에 #ec2 인스턴스 #ami #생성 #firewall
- haproxy #wordpree #php #linux #가상화 #가상머신 #내용정리
- 스파르타코딩클럽 #부트캠프 #IT #백엔드 #머신러닝 #AI #서버 #자동화 #SQL #기본문법 #데이터베이스 #웹개발
- Today
- Total
요리사에서 IT개발자로
테스트 코드 작성? (단위테스트 JUnit) 본문
테스트 코드가 비즈니스 로직의 구현체에 대한 피드백을 주는것과 같다.
기존 비즈니스로직이 클래스 또는 Repository 등등 의존성이 너무 심한 경우
기본적으로
테스트 코드 작성을 하기에 비교적으로 용이하지 않다.
또한 @Mock 을 비교적으로 편리하게 관리하게 하기 위한 방법은 따로 존재하지 않는다.
비즈니스로직은 언제든지 바뀔 수 있기에 테스트코드가 깨질 수 는 있다.
슬라이스 테스트를 하는 이유
한 구간에서 모든것을 풀 테스트를 하게된다면
@Mock에 대한 의존성이 높아지기 때문이다.
Mock 가짜객체
테스트할 때 필요한 실제 객체와 동일한 모의 객체를 만들어 테스트의 효용성을 높이기 위해서 사용한다.
사용 경우
- 실제 객체를 만들기에 비용과 시간이 많이 소요되는 경우
- 의존성이 길게 걸쳐져 있어서 테스트를 제대로 구현하기 여러울 때
- 테스트 작성을 위한 환경 구축이 어려운 경우
종유 및 사용방법
- @Mock :특정 클래스 위에 선언하면 해당 클래스를 가짜 객체로 만들어준다.
- @InjectMocks : @Mock으로 생성된 객체를 자동으로 Di 해주는 어노테이션이다.
@Mock
private PostLikeRepository postLikeRepository;
@Mock
private UserRepository userRepository;
@Mock
private PostRepository postRepository;
@InjectMocks
private PostLikeServiceImpl postLikeService;
Mock을 사용하기 위해서는
@ExtendWith(MockitoExtension.class)
class PostLikeServiceImplTest {
클래스에
@ExtendWith(MockitoExtension.class)를 붙어주어야한다.
@MockBean은 통합테스트에서 쓰인다.
단위테스트를 구성하는 방법
AAA패턴 사용
준비(Arrange), 실행(Act), 검증(Assert)
익숙해지면 모든 테스트를 쉽게 읽을 수 있고 이해할 수 있기에
유지 보수 비용이 줄어든다.
public class Calculator {
public int sum(int firstNum, int secondNum) {
return firstNum + secondNum;
}
}
class CalculatorTest {
@Test
void sum_of_two_numbers() { // 단위 테스트 이름
// Arrange (준비 구절)
int firstNum = 10;
int secondNum = 20;
Calculator calculator = new Calculator();
// Act (실행 구절)
int result = calculator.sum(firstNum, secondNum);
// Assert (검증 구절)
assertThat(result).isEqualTo(30);
}
}
준비구절
테스트 대상 시스템(SUT : System Under Test)과 관련된 의존성을 원하는 상태로 만든다.
실행 구절
SUT에서 메서드를 호출하고 의존성을 전달하며 출력 값을 캡쳐한다.
검증 구절
결과를 검증한다.
결과는 반환 값이나 SUT와 협력자의 최종상태, SUT가 협력자에 호출한 메서드 등으로 표시 될 수 있다.
Given -When-Then패턴
AAA와 똑같다고 보면된다
Given - 준비 구절
When - 실행 구절
Then - 검증 구절
테스트 구성 측면에서 차이는 없으나
비개발자에게는 Given - When - Then 구조가 더 읽기 쉽다.
@Test
@DisplayName("게시글 좋아요 삭제 성공 테스트")
void deleteLikeFromPost() {
//given
given(postRepository.findById(1L)).willReturn(Optional.of(post));
given(postLikeRepository.findByPostAndUser(post, userDetails.getUser())).willReturn(
Optional.of(postLike));
//when
postLikeService.deleteLikeFromPost(1L, userDetails);
//Then
assertThat(post.getCount()).isEqualTo(0L);
}
테스트 코드 작성중의 피해야 될 상황
1. 여러개의 준비, 실행, 검증 구절 피하기(통합테스트)
2. 테스트 내 if문 피하기(테스트 내의 또 테스트가 진행한다?)
준비 구절과 실행 구절
AAA패턴을 적용했을 경우 각 구절의 크기와 구성
준비구절이 가장 크다.
그러므로 같은 테스트 클래스 내 공개 메서드 또는 별도의 팩토리 클래스로 도출하는것이 좋다.
준비 구절에서 코드 재사용이 도움이되는 두가지 패턴에는
Object Mother와 Test Data Builder가 있다.
public class TestUsers {
public static User aRegularUser() {
return new User("John Smith", "jsmith", "42xcc", "ROLE_USER");
}
public static User anAdmin() {
return new User("James Smith", "jsmith", "42xcc", "ROLE_ADMIN");
}
// other factory methods
// ...
}
테스트 코드를 작성하다보면 똑같은 객체를 계속해서 생성해주어야 하는 상황이 발생할 것이다.
이것을
Object Mother를 사용하여 테스트 사용될 객체를 재사용함으로
데이터의 생성과 관리를 일관성 있게 할 수 있다.
Test Data Builder패턴은
Builder를 활용하여 테스트 데이터를 생성하는 패턴
public classUserStubGenerator {
public static UserStub.BuildergenerateUserStub() {
return UserStub.builder()
.loginId("tid1234")
.password("password1234@");
}
}
위와 같이 데이터를 만들고
// 사용처 (loginId 특수문자가 있을 시 실패하는 케이스)
User testUser = UserStubGenerator.generateUserStub()
.loginId("@#($*$(@)")
.toEntity();
해당 빌더를 재사용하여 Entity를 생성할 수 있으나
테스트 객체의 상태를 확인하기 어렵다는 단점이 있다.
실행구절
보통 코드 한줄로 이루어져 있고 두 줄 이상인 경우에는 SUT구성에 문제가 있을 수 있다.
@Test
void purchase_succeeds_when_enough_inventory() {
// Given (준비 구절)
Store store = new Store();
store.AddInventory(Product.Shampoo, 10);
Customer customer = new Customer();
// When (실행 구절)
boolean success = customer.purchase(store, Product.Shampoo, 5); // 상품 구매
store.removeInventory(success, Product.shampoo, 5); // 재고 감소
// Then (검증 구절)
assertThat(success).isTrue();
assertThat(store.GetInventory(Product.Shampoo)).isEqualTo(5);
}
위의 테스트 코드의 문제점
상품 구매와 재고 감소가 동시에 있다.
단일 작업(구매 프로세스)을 수행하는데 두개의 메서드 호출이 필요하단것.
테스트 자체는 문제가 없을 수 있으나
단위 테스트는 구매 프로세스 라는 단일한 동작 단위를 검증하는 것이다.
해결책은 코드 캡슐화를 항상 지키는 것이다.
이처럼 단위 테스트 작성은 비즈니스 로직의 리팩토링 신호를 파악할 수 있다는 장점이 있다.
검증 구절
단위 테스트의 단위는 동작의 단위이다. 코드의 단위가 아니다.
단위 동작은 여러 결과를 낼 수 있고 모든 결과를 평가하는 것이 좋다.
// Assert v1
assertThat(customer.getId()).isEqualTo(1L);
assertThat(customer.getName()).isEqualTo("스파르타");
assertThat(customer.getPhone()).isEqualTo("010-1234-1234");
assertThat(customer.getBudget()).isEqualTo(5000);
assertThat(customer.getInventorySize()).isEqualTo(5);
위와 같은 방법보단
// Assert v2
assertThat(customer.equals(afterPurchase)).isTrue();
테스트의 크기가 너무 커져버리면 테스트를 한눈에 파악 하기 어려워지기 때문에
필드가 너무 많다면 적절하게 객체를 정의하는 방법도 있다
SUT를 구별하기 위해서는 다른 클래스 들과 구분하는 것이 중요하다.
테스트에 개입하는 클래스들이 많다면 테스트 대상을 찾는것에 시간이 많이 들일 수 있기에
내가 테스트하고싶은 대상(클래스)의
변수명을 sut로 사용하는것이 좋다.
@Test
void sum_of_two_numbers() {
// Given
int first = 10;
int second = 20;
Calculator sut = new Calculator();
// When
int result = sut.sum(first, second);
// Then
assertThat(result).isEqualTo(30);
}
}
마지막으로
SUT를 구별하는 것이 중요하듯 테스트 내에서 특정 부분이 어떤 구절속에 있는 지 파악하기에 시간을 많이 들이지 않도록
각 구절을 구분하는 것이 중요하다.
AAA 또는 given-when-then패턴이 익숙해졌다면
빈줄로 분리하는 것이 코드량이 적고 가독성도 좋다.
@Test
void sum_of_two_numbers() {
int first = 10;
int second = 20;
Calculator sut = new Calculator();
int result = sut.sum(first, second);
assertThat(result).isEqualTo(30);
}
}
'Spring' 카테고리의 다른 글
테스트 코드의 중요성 (0) | 2024.07.05 |
---|---|
Spring Boot @Transactional 요약 (0) | 2024.07.02 |
스파르타 코딩클럽 JPA 심화 JPARepository 기능 (0) | 2024.06.27 |
스파르타 코딩클럽 JPA 심화 트랜잭션 (0) | 2024.06.26 |
SQL 쿼리 delete메소드 작동에 대해 (0) | 2024.06.25 |