Post

TIL(20240705) [테스트코드의 중요성]

TIL(20240705) [테스트코드의 중요성]

📌 테스트코드의 중요성

  1. 테스트의 부재시 발생하는 문제
    • 프로젝트의 규모가 커질수록 간단한 기능 추가 및 수정 시 다른 기능에 어떤 영향을 끼칠 지 알 수 없다.
    • 원래 잘 동작하던 기능에도 이상이 생길 수 있다.
    • 즉, 어떤 사이드 이펙트가 따라올 지 알 수 없다.
    • 복잡한 기능일수록 내가 구현한 코드가 의도한대로 잘 동작하는 지에 대한 검증이 꼭 필요하다.
  2. 테스트를 포함하게 되면?
    • 테스트가 프로젝트의 안정성을 유지해주는 역할을 하기 때문에 새로운 기능을 도입하거나 요구사항에 잘 맞게 리팩토링한 후에도 기존기능이 잘 동작하는지 확인하는 데 도움이 된다.
    • 테스트 초반에는 노력과 시간이 필요하다는 단점이 있지만
    • 장기적으로 보았을 때 프로젝트 후반까지 코드를 안전하게 확장시킬 수 있다는 점에서 장점이 됩니다.

위와 같이 중요성에 대해서 알아보았는데,
중요성에 대해서 정리해보자면

💡 내가 구현한 코드가 복잡한 기능일수록 의도대로 잘 동작하는지 검증이 꼭 필요하다. 그리고 프로젝트의 규모가 커질수록 간단한 기능 추가 시 사이드 이펙트가 따라올 수 있기에 이러한 상황에 대비하기 위해 test코드를 잘 작성해야한다.

그럼 어떻게 테스트코드를 작성하면 좋은 테스트코드라고 할 수 있을지 알아보자.

🔔 단위테스트

  • 단위테스트란? 하나의 기능, 하나의 모듈이 올바르게 잘 동작하고 있는지 확인하는 독립적인 테스트(연관된 객체들을 가짜객체로 대체함)
  • 간단한 Dto나 util클래스는 단위테스트를 작성하게 되면 가치가 0일 확률이 높다.

  • 좋은 단위테스트의 4가지 특성? 1) 회귀방지 -> 기능 오류를 방지 2) 리팩토링 내성 -> 리팩토링 해도 테스트가 깨지지 않음 3) 빠른 피드백 -> 빠른 테스트 속도⭐⭐ 4) 유지보수성 -> 가독성과 재사용서잉 좋고 실행되기 쉽게 작성되어야 함

  • 리팩토링 내성을 높이는 방법은 구현 세부사항 대신, 최종결과를 목표로 테스트 코드를 작성하는 것
  • 하지만, 테스트코드가 모든 기능 오류를 방지해주는 것은 아니다.

이렇게 좋은 단위테스트의 4가지 특성에 대해서 알아보았고, 각 특성들의 세부적인 것들을 알아보도록 하자.

🚩 빠른 피드백 ?

  • 의존성이 없는 코드에 대한 테스트 작성(POJO테스트)
  • 의존성을 mock처리하여 핵심 비지니스 로직에 대한 테스트코드만 작성 ex) 복잡한 비지니스 로직이 포함된 service클래스
1
**POJO테스트?  Plain Old Java Object로 순수 자바코드로 이루어진 테스트 의미함

📕 Mock 사용하는 경우 ?

  • 실제 객체를 만들기에는 비용과 시간이 많이 소요되는 경우
  • 의존성이 길게 걸쳐져 있어서 제대로된 테스트 구현이 어려울 경우
  • 테스트 작성을 위한 환경구축이 어려운 경우
  • 사용방법 mock()/ @InjectMoks, @Mock
1
2
3
4
5
6
7
8
9
10
11
12

public class OrderServiceTest {

    private OrderRepository orderRepository = mock(OrderRepository.class);
    private ProductRepository productRepository = mock(ProductRepository.class);
    private OrderLineRepository orderLineRepository = mock(OrderLineRepository.class);

    private OrderService orderService = new OrderService(orderRepository, productRepository, orderLineRepository);
		
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ExtendWith(MockitoExtension.class) // Mock Application Context Load
public class OrderServiceTest {

    @Mock
    private OrderRepository orderRepository;
    @Mock
    private ProductRepository productRepository;
    @Mock
    private OrderLineRepository orderLineRepository;

    @InjectMocks
    private OrderService orderService;
 
 
}   

mock()과 @Mock은 동일한 기능을 하지만 @Mock 사용 시, 꼭 test위에 @ExtendWith(MockitoExtension.class) 해당 어노테이션을 붙여주어야 한다.

@MockBean 어노테이션은 뭐지?

  • 통합테스트에 쓰이며 실제 Spring Application Context에 올라와있는 Bean을 Mock으로 처리함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ExtendWith(SpringExtension.class) // 실제 Application Context Load
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;
    
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private ProductRepository productRepository;
    @MockBean
    private OrderLineRepository orderLineRepository;
 

}   

@ExtendWith(SpringExtension.class) 어노테이션을 달아주면 @Autowired로 설정된 객체들은 실제 의존성을 주입받지만 @MockBean 어노테이션을 달아주는 객체는 Mock으로 사용하겠다는 의미로, 반환값을 설정해주어야 한다!

This post is licensed under CC BY 4.0 by the author.