TDD란?

Chunbae·2024년 10월 4일
1

개발이론

목록 보기
2/6

TDD?

TDD는 Test Driven Development의 약자로 테스트 주도 개발이라고 한다.

테스트 주도 개발이란 반복 테스트를 이용한 소프트웨어 방법론으로 작은 단위의 테스트케이스를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복하여 구현한다.

짧은 개발주기의 반복에 의존하는 개발 프로세스이며 애자일(Agile)방법론 중 하나인 eXtream Programming(XP)의 Test - Frist 개념에 기반을 둔 단순한 설계를 중시한다.

eXteam Programming - XP란?
미래 예측을 최소화하고 지속적으로 프로토타입을 완성하여 추가 요구사항이 생기더라도 실시간으로 반영할 수 있는애장리 기법중 하나이다.


TDD의 개발 주기

<RED>
실패하는 테스트코드를 먼저 작성한다.
<Green>
테스트 코드를 성공시키기 위한 코드를 작성한다.
<Refactor>
중복 코드 제거, 일반화 등의 리펙토링을 수행한다.

주의사항
실패하는 테스트 코드를 작성할 때까지 실제 코드를 작성하지 않는다.
실패하는 테스트를 통과할 정도의 최소 실제 코드를 작성한다.

이를 통해 명확하게 정의할 수 있고 불필요한 설계를 피하고, 정확한 요구 사항에 집중이 가능해진다.


일반개발 방법과 TDD개발 방법 비교


일반 개발

일반적인 개발 방법은 요구사항 분석 -> 설계 -> 테스트 -> 배포 의 형태인 개발 주기를 갖는다.
이러한 방법은 소프트웨어 개발을 느리게 하는 잠재적인 위험이 있다.

  • 소비자의 요구사항이 처음부터 명확하지 않다.
  • 따라서 완벽한 설계는 불가능하다.
  • 자체 버그 검출 능력저하 또는 소스코드의 품질이 저하된다.
  • 자체 테스트 비용이 증가한다.

이러한 문제점이 발생되는 이유는 "어느 프로젝트든 초기 설계가 완벽하다고 말할 수 없기 때문이다."
고객의 요구사항 또는 디자인 오류등에 내외부 조건에 의해 재 설계하여 점진적 설계를 진행한다.

이때 재설계를 하는 과정에서 개발자는 코드의 삽입, 수정, 삭제하는 과정에서 불필요한 코드를 남기거나 중복처리 되는 가능성이 있다.
또한, 작은 부분의 수정에도 모든부분에서 기능테스트진행해야 하기 때문에 전체적인 버그를 검출하기 어려움이 있어 버그 검출 능력이 저하된다. 그 결과 어디서 어떤 버그가 발생될지 모르는 상황에서 코드를 수정하지 않는 형상이 나타나고 이러한 현상이 누적되는 결과 소스코드의 품질이 저하되는 문제가 발생한다.


TDD개발

일반 개발방법과 가장 큰 차이점은 테스트 코드를 작성한 뒤에 실제 코드를 작성한다는 점이다.

  • 설계 단계에서 프로그래밍의 목적을 미리 정의한다.
  • 무엇을 테스트해야 할지 미리 정의한다.
  • 테스트 코드를 작성 중 발생하는 예외에 대한 테스트 케이스를 추가하고 설계를 개선한다.
  • 테스트가 통과된 코드만 코드 개발 단계에서 실제 코드로 작성한다.

이러한 과정을 통해 자연스럽게 코드의 버그가 줄어들고 소스코드의 품질이 향상된다.


TDD 개발 예시

간단한 CRUD기능을 Spring Boot에서 TDD주도 개발을 구현하는 방법이다.

사용한 프레임워크는 JunitMockito를 사용했다.

1. 테스트 작성 (Red)

아직 Service Class의 CreateItem은 생성하지 않은 상태에서 테스트 코드를 작성을 진행한다.

//Test코드를 명시
@SpringBootTest
public class ItemServiceTest{
	
    @Mock
    private ItemRepository itemRepo;
    
    @InjectMock
    private ItemService itemService;
    
    @Test //실행되는 테스트 코드
    public void testCreateItem(){
    
    	//Given : 사전에 준비 조건, 테스트를 위한 객체
        Item item = new Item("TestItem", "TestDescription");
        
        //아이템이 저장될 때 더미 데이터로 저장되도록 설정
        when(itemRepo.save(any(Item.class))).thenReturn(item);
        
        //When : 아잍메을 생성하는 메서드 실행
        Item createItem = itemservice.createItem(item);
        
        //Then : 아이템 생성이 정상적으로 이루어졌는지 검증
        assertEquals("TestItem", createItem.getName());
        //save메서드가 한번 호출되었는지 확인
        verify(itemRepo, times(1)).save(item);
	}
}

2. 기능 구현(Green)

ItemService에서 createItem메서드를 구현하여 테스트를 성공하도록 함.

@Service
public class ItemService{
	
    @Autowired
    private ItemRepository itemRepo;
    
    public Item createItme(Item item){
    	return itemRepo.save(item);
	}
}

3. 리펙토링(Blue)

코드가 성공적으로 테스트를 통과하면 코드의 구조나 성능을 개선하기 위해 진행한다.
리펙토링을 진행한 이후 테스트를 다시 실행하여 기능이 제대로 유지되는지 확인해야한다.


TDD패턴

테스트를 구조화하고 가독성을 높이때 사용한다. 다양한 패턴이 있지만 예시코드에서 사용한 패턴은 "Given - When - Then"패턴이다.

  • Given
    • 테스트의 사전조건이나 설정을 의미한다. 테스트를 실행하기 전에 특정한 상태를 설정하거나, 데이터가 필요하거나, 특정 객체가 초기화 되어야할 때 사용한다.
  • When
    • 실제로 실행되는 기능을 나타낸다. 주로 테스트하려는 메서드를 호출하거나 행동을 유발하는 코드를 위치 시킨다.
  • Then
    • 예상되는 결과나 상태를 검증하는 부분이다. assert구문을 사용하여 원하는 결과가 실제로 출력이되는지 확인한다.

(추가) Mock?

Mocking은 테스트를 진행할 때 실제 구현 대신 가짜 객체(Mock Object)를 만들어 사용하는 기법이다.
해당 기법은 실제 객체의 동작을 시뮬레이션함으로써 테스트 대상과 의존성이 있는 외부 요소나 서비스에 대해 제어를 할 수 있게 된다.

실제 객체처럼 동작하지만 테스트하고자 하는 코드에 외부 종속성이 미치는 영향을 제거하거나 예외 상황을 테스트를 진행하도록 사용자설정이 가능하다.

사용이유?

  1. 의존성 분리
    실제환경에서 동작하는 외부 시스템은 테스트 중에 제어하기어렵거나 실행시간이 오래 걸릴 수 있다. 이러한 점을 줄이기 위해 Mocking하여 실제 동작을 모방함으로써 시간을 단축하고 제어가 쉽도록 설정한다.

  2. 독립성 보장
    Mock객체를 사용하면 테스트가 외부시스템에 의존하지 않는다. 각테스트가 독립적으로 실행가능하며 서로 간섭하지 않도록 해준다.

  3. 예외 테스트
    외부 서비스가 실패하거나 예외를 던지는 상황에서 직접적으로 재현하기 어려운 점이 있다. 이때 Mock객체는 이러한 예외 상황을 쉽게 설정하고 테스트하도록 설정한다.

  4. 상태 검증
    특정 메서드가 얼마나 호출되었는지, 특정 조건에 따라 동작했는지 검증이 가능하다.


어노테이션과 메서드

  • 어노테이션
    • @Mock
      Mock객체를 생성할 때 사용한다. 해당 객체는 실제 구현 대신 테스트에 사용된다.
    • @InjectMock
      테스트하려는 객체에 Mock객체를 주입할때 사용한다.
      주로 서비스 클래스나 테스트 대상 클래스에서 주입하는 객체들을 모킹하여 테스트한다.
  • 메서드
    • when...thenReturn
      when구문을 사용하여 Mock객체의 특정 메서드 호출에 대해 가짜 응답을 설정한다.
      예시코드를 보면 itemRepo.save()가 호출되면 미리 설정한 item객체를 반환하도록 설정했다.
    • verify
      특정 메서드가 호출되었는지 검증한다. 해당 메서드를 통해 Mock객체의 메서드가 정확히 몇번 호출되었는지 어떤 매개변수를 사용했는지 확인이 가능하다.

Mocking은 외부의 의존성을 제어하고, 테스트환경을 보다 쉽게 구성할 수 있도록 한다. 이를 통해 코드의 로직에 집중한 테스트가 가능하며 테스트의 독립성을 보장한다.



참고문서

profile
말하는 감자

0개의 댓글