티스토리 뷰
이번 글에서는 박우빈 님의 "Practical Testing: 실용적인 테스트 가이드"를 보고 테스트 코드에 대한 제 생각을 정리해 보려고 합니다.
테스트 코드란?
개발자가 작성한 실제 코드가 제대로 동작하는지 검증하는 코드입니다.
테스트 코드와 관련된 TDD, BDD와 같은 개발 방법론이 존재합니다.
기술을 학습할 때 테스트 코드를 사용하면 여러 가지 경우를 직접 설정해 구체적인 기능을 재밌게 학습할 수 있는 장점도 있습니다.
테스트 코드를 작성하지 않으면 발생하는 일
다음은 제가 겪었던 일입니다.
학교에서 사용 중인 프로젝트에 테스트 코드를 전혀 작성하지 않고, 프로덕션 코드 작성 후 postman을 사용해서 성공 및 간단한 예외 케이스만 확인한 후 바로 배포를 했습니다.
당시에는 테스트 코드의 중요성을 못 느껴서 작성하지 않고 진행했는데 지금 와서 보니 오류가 언제 발생할지 모르는 프로젝트가 되었고 개발하는 시간보다 예상치 못한 곳에서 터지는 오류를 고치는 시간이 더 많아졌습니다.
테스트 코드가 없으면 내 코드가 의도한 대로 동작하는지 검증할 방법이 없어 경험과 감에 의존한 채 개발을 진행하게 되고 결국에는 불안정한 서비스가 될 것입니다. 그리고 오류를 배포한 후에 확인할 수 있어서 서비스에 대한 피드백이 늦고, 유지보수가 점점 어려워지며 서비스의 품질이 떨어질 것입니다.
테스트 코드를 작성해야 하는 이유
테스트 코드 == 문서화
@SpringBootTest // 서버를 띄워 통합 테스트를 진행
class ProductServiceTest {
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@AfterEach // 각 테스트가 끝난 후 실행
void tearDown() {
productRepository.deleteAllInBatch();
}
@DisplayName("신규 상품을 등록한다. 상품번호는 가장 최근 상품의 상품번호에서 1 증가한 값이다.")
@Test
void createProduct() {
// given
Product product = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
productRepository.save(product);
ProductCreateServiceRequest request = ProductCreateServiceRequest.builder()
.type(HANDMADE)
.sellingStatus(SELLING)
.name("카푸치노")
.price(5000)
.build();
// when
ProductResponse productResponse = productService.createProduct(request);
// then
assertThat(productResponse)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains("002", HANDMADE, SELLING, "카푸치노", 5000);
List<Product> products = productRepository.findAll();
assertThat(products).hasSize(2)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.containsExactlyInAnyOrder(
tuple("001", HANDMADE, SELLING, "아메리카노", 4000),
tuple("002", HANDMADE, SELLING, "카푸치노", 5000)
);
}
@DisplayName("상품이 하나도 없는 경우 신규 상품을 등록하면 상품번호는 001이다.")
@Test
void createProductWhenProductIsEmpty() {
// given
ProductCreateRequest request = ProductCreateRequest.builder()
.type(HANDMADE)
.sellingStatus(SELLING)
.name("카푸치노")
.price(5000)
.build();
// when
ProductResponse productResponse = productService.createProduct(request.toServiceRequest());
// then
assertThat(productResponse)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains("001", HANDMADE, SELLING, "카푸치노", 5000);
List<Product> products = productRepository.findAll();
assertThat(products).hasSize(1)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains(
tuple("001", HANDMADE, SELLING, "카푸치노", 5000)
);
}
상품을 등록하는 테스트 코드입니다. BDD 방식(given-when-then)으로 작성했습니다.
상품을 등록하는 기능을 포함해서 전체적인 서비스를 출시하고 유지보수를 해야 한다는 가정을 해보겠습니다. 그렇게 되면 서비스 인수인계를 해야 할 때가 올 것입니다. 서비스를 넘겨받은 개발자는 서비스 코드를 이해하는 시간이 필요한데, 이때 테스트 코드가 도움을 줄 수 있습니다.
테스트 코드를 보면 당시 개발자가 어떤 흐름으로 서비스를 구성했는지 알 수 있습니다. 결국 테스트 코드가 있게 되면 서비스를 넘겨받은 개발자는 쉽게 서비스에 적응할 수 있을 것입니다. 그리고 서비스를 유지 보수하는 개발자도 서비스를 더 잘 이해하면서 코드를 작성할 수 있습니다.
빠른 피드백
테스트 코드가 없어 코드를 바로 배포하게 되면 예상치 못한 오류(예외 처리를 하지 않았거나, 유효성 검증을 하지 않거나 등)를 만나게 될 확률이 높습니다. 그렇게 되면 서비스 품질과 서비스에 대한 사용자의 신뢰도가 굉장히 낮아질 것입니다.
하지만 테스트 코드를 작성하면 실제 코드가 의도한 대로 동작하는지 바로 확인할 수 있어 예상치 못한 오류를 만날 확률이 낮습니다.
과감한 리팩토링
테스트 코드가 의도한 대로 동작하면 위 이미지처럼 초록불이 뜨게됩니다. 테스트 코드를 제대로 작성했다는 가정하에, 프로덕션 코드를 과감하게 리팩토링해도 초록불만 확인하면 코드가 제대로 작동하는지 검증할 수 있습니다.
실제 코드 작성 -> 테스트 코드 작성 -> 초록 불 확인 -> 리팩토링 -> 초록 불 확인 ..
위 사이클을 반복하면서 서비스를 구축하고 리팩토링 하면, 리팩토링 시간을 단축하고 전보다 클린한 코드를 작성할 수 있을 것입니다.
안정적인 서비스
테스트 코드를 작성하면 개발자는 언제든지 테스트 코드를 실행해서 실제 코드가 의도한 대로 동작하는지 확인할 수 있습니다.
유지보수하는 과정에서 기존 코드가 수정될 수 있는데 테스트 코드를 실행해서 프로덕션 코드가 잘 작동하는 걸 확인하면 안정적인 서비스를 구축할 수 있을 것입니다. 또한 빠르게 변화하는 소프트웨어의 안정성을 테스트 코드를 통해 보장할 수 있습니다.
마무리
전에 헥사고날 아키텍처를 도입할 때 테스트 코드 없이 수동으로 테스트를 진행해서 리팩토링을 하기 힘들었던 경험이 있습니다.
제가 생각하기에 개발자에게 중요한 것 중 하나는 자신이 하는 일에서 반복되는 것을 자동화를 통해 시간을 단축하고 실수를 줄이는 게 중요하다고 생각합니다.
테스트 코드는 수동으로 테스트한 것을 이른 시간 안에 자동으로 확인해 주기 때문에 좋은 서비스를 개발할 수 있도록 해주는 것 같습니다. 공부하면서 느끼는 거지만 테스트 코드는 정말 필수인 것 같습니다.
이 글을 읽고 계신 분들 중 아직 프로젝트에 테스트 코드를 작성하지 않으셨다면 이번 기회에 프로젝트에 테스트 코드를 적용해서 다양하고 좋은 경험을 해보시면 좋을 것 같습니다!
'개발 이야기' 카테고리의 다른 글
인텔리제이 자주 사용하는 코드 템플릿으로 만들기 (0) | 2024.05.30 |
---|---|
계층형 아키텍처에서 헥사고날 아키텍처 도입하기 (0) | 2024.05.28 |
- Total
- Today
- Yesterday
- IDE
- propagation
- 테스트 코드
- JPA
- 계층형 아키텍처
- 헥사고날 아키텍처
- Slack
- detekt
- webhook
- embedded redis
- N + 1
- H2
- 자동화
- Spring Boot
- transaction
- lombok
- multi module
- ktlint
- 인텔리제이
- 성능 개선
- Gradle
- lint
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |