[Test] Spring boot 테스트 기본 개념 정리

썸네일

 

테스트 프레임워크에 대해서 기본 개념에 대해서 학습 정리한 포스트입니다.

 

🐻테스트 프레임워크

스프링부트 애플리케이션을 만들고, 테스트 코드를 작성하여 요구사항대로 코드가 정상 작동하는지 검사하기 위해서 테스트 코드를 작성한다. 이 테스트 코드를 작성하는데 주로 사용하는 프레임워크에 대해서 개념정리를 합니다.

 

🐻JUnit5 : 단위 테스트 코드 작성, 어노테이션 

전체적으로 JUnit 5 공식 문서와 AssertJ, Mocito를 읽어본 경험이 있다. 당시에는 실제 사용하기 전에 어떤 게 있는지, 어떻게 사용하는지 알아보기 위해서 가볍게 읽어보았던 것 같다. 

프로젝트를 진행하고 부트캠프에서 미션을 진행하면서 실제 사용해 보면서 많이 익숙해졌는데, 개념적으로 한 번 정리하고 자주 쓰는 어노테이션에 대해서 정리해 보면 좋을 것 같다.

 

🐻 @ParameterizedTest

테스트에 필요한 매개변수를 전달해 주는 어노테이션이다. 한 개의 테스트 메서드가 다수의 테스트 케이스를 가지는 경우 사용하면 유용하다. 테스트 케이스별 상이하게 적용해야 하는 테스트 데이터는 Parameter로 전달할 수 있어서 중복을 제거할 수 있다.

 

예를 들어서, 특정 객체를 생성하는데 인스턴스 필드를 초기화하는데 글자 제한이 2~10글자 사이만 생성할 수 있는지 테스트하는 케이스를 생각해 보자. Null 이 들어오거나, Empty, 1글자 11글자 가 들어오는 경우에는 실패하는 테스트 케이스를 만들어야 한다. 이러한 테스트 코드를 하나씩 따로 만들게 된다면 동일한 기능을 반복적으로 작성하게 된다.

 

사용하는 방법은 다음과 같다.

@ParameterizedTest
@ValueSource(Strings = "{}")

 

  • Test Method에 전달할 값을 어노테이션의 배열로 지정한다.
  • Test Method는 배열 길이만큼 실행되고, 배열의 요소가 하나씩 인자 값으로 전달된다.
  • TYPES는 인자 값으로 사용할 타입의 복수 명사(ints, strings, bytes, etc..)로 선언하면 된다.
@ParameterizedTest
@CsvSource(value = {"1,2", "2,4", "3,6"})
  • 하나의 Test Method에 두 개의 인수를 전달해야 하는 경우 사용한다.
  • CSV 파일 형식처럼 구분자(delimeter)는 기본적으로 쉼표이고 옵션을 통해서 구분자를 커스텀할 수 있다.

 

@RepeatedTest

지정된 횟수만큼 반복적으로 실행되도록 설정하는 어노테이션이다. 

10번 반복해서 테스트해야 하는 경우 for 구문을 사용해서 Test Code를 작성할 수 있지만, 코드의 가독성이 떨어진다. RepeatedTest 어노테이션을 사용하면 for 구문 없이 반복 실행할 수 있다.

@RepeatedTest(value=10, name=RepeatedTest.LONG_DISPLAY_NAME)
void repeatTest(RepetitionInfo repetitionInfo) {...}
  • value = 반복 횟수
  • name = RepeatedTest.LONG_DISPLAY_NAME : Test 명을 명시한다.
    • Default(SHORT) : "repetition" + 현재 반복 + " of" + 총 반복 횟수
    • LONG : @DisplayName + SHORT_DISPLAY_NAME

 

@TestFactory

동적으로 Test Method를 생성하는 팩토리 메서드를 정의하는 어노테이션

  • 동적으로 다양한 Test Case를 검증해야 하는 경우
  • 인수 테스트처럼 사용자 시나리오를 테스트하는 경우
@TestFactory
Stream<DynamicNode> testFactory() {...}

 

  • Test Case를 생성하는 팩토리임을 알리는 기능을 한다.
  • @TestFactory 어노테이션이 붙은 메서드는 private 및 static으로 선언할 수 없다.
  • Return Type: Stream, Collection, Iterable, Iterator
  • Return Type 이 Collections 타입을 반환하지 않는 경우 JunitException이 발생하면서 테스트가 Fail로 종료된다.

 

@Timeout

단위 테스트에 대한 시간제한을 설정하는 어노테이션이다. 코딩테스트 같은 시간 제한을 설정할 경우에 유용하다.

 

실행 시간이 중요한 기능의 단위 테스트에 적용한다.

주어진 시간 안에 테스트가 끝나지 않으면 Error가 발생하고 실패한다.

java.util.concurrent.TimeoutException

 

@Test
@Timeout(3) // 시간 단위 : second(초)
void timeoutExampleTest() throws InterruptedException {
	Thread.sleep(4000); // 시간 단위 : Millisecond
}

 

@ExtendWith

Test Class에서 공통적으로 사용할 확장 기능을 설정하는 어노테이션

@ExtendWith(Mocito.class)

Mock 객체를 쉽게 사용할 수 있도록 제공하는 프레임워크의 기능을 JUnit에서도 사용할 수 있도록 확장해 주는 역할

 

🐻TDD와 BDD

  • BDD는 사용자의 행동까지 생각하고 Test 하면서 개발하는 방법론이다.
  • BDD는 TDD에서 파생된 개발 방법론

언제 사용하는가

  • 프로젝트에서 BDD의 Test Case로 시나리오를 검증하고, 시나리오에서 사용되는 각 모듈들은 TDD의 Test Case로 검증하는 방식이다.
  • 예시 : 로그인
    • BDD 방식 (ID와 Password가 주어졌을 때, 로그인 시도를 하고 로그인 정보가 유효하다면 Access Token을 반환)
    • TDD 방식
      • 회원 여부를 확인하는 Test Case를 통과하는 단위 테스트를 작성하여 로그인 기능의 모듈인 테스트 메서드 구현

 

🐻 코드

@ParameterizedTest

public class JUnitExampleTest {
	
    @ParameterizedTest
    @ValueSource(Strings = {" ", "일"})
    @NullAndEmptySource
    void createUserException(String username) {
    	assertThatThrownBy(() -> new User(username))
        	.isInstanceOf(IllegalArgumentException.class);
    }
    
    @ParameterizedTest
    @CsvSource(value = {"1, 2", "2, 4", "3, 6"})
    void csvSourceTest(int input, int expected) {
    	assertThat(input*2).isEqualTo(expected);
    }
}

 

assertJ 단언 메서드

  • isEqualTo(e)
  • contains(e)
  • doesNotContain(e)
  • startWith(e)
  • endWith(e)
  • isNotEmpty()
  • isPositive(n)
  • isGreaterThat(n)

 

@RepeatedTest

@DisplayName("RepeatTest#1")
@RepeatedTest(value = 10, name=RepeatiedTest.LONG_DISPLAY_NAME)
void repeatTest(RepetitionInfo repetitionInfo) {
	repetitionInfo.getCurrentRepetition();
    repetitionInfo.getTotalRepetitions();
}
  • 다른 Test Method와 큰 차이점은 RepetitionInfo 인자 값이 존재한다는 것
  • value가 10으로 할당되었기 때문에 1부터 10까지 1씩 증가하면서 반복 실행한다.
  • RepetitionInfo.getCurrentRepetition()을 통해서 현재 value를 가져올 수 있다.

 

@TestFactory

  • 다른 Test Method와 달리 Return Type이 void 타입이 아닌 Collections 타입이다.
  • 반복하면서 dynamicTest 메서드를 통해서 n개의 Test Method를 생성하고 실행한다.
@TestFactory
List<DynamicNode> testFactory() {
	return List.of(
    	dynamicTest("testName", () -> test());
    );
}
@SpringBootTest
@Transactional
@AutoConfigureTestDatabase(
	replace=AutoConfigureTestDatabase.Replace.ANY,
    connection=EmbeddedDatabaseConnection.H2
)
@ActiveProfiles("test")
public class TestFactoryExample {
	
    @Autowired
    private PerformanceRepository performanceRepository;
    
    @Autowired
    private TicketingService ticketingService;
    
    @TestFactory
    Stream<DynamicTest> 통합_테스트_예시1() {
    	return Stream.of(
        	dynamicTest("공연 정보 저장 테스트", ()-> {
            	// given
                Performance performance = new Performance();
                // when
                performanceRepository.save(performance);
                Performance insertedPerformance = performanceRepository.findByName("레베카");
                // then
                assertNotNull(insertedPerformance);
                assertEquals(performance.getName(), insertedPerformance.getName());
                assertEquals(performance.getStartDate(), insertedPerformance.getStartDate());
            },
            dynamicTest("공연 예약 테스트", () -> {
            	// given
                Performance insertedPerformance = performanceRepository.findByName("레베카");
                Ticket ticket = new Ticket();
                // when
                Ticket reservedTicket = ticketService.ticketing(ticket);
                // then
                assertEquals(ticket, reservedTicket);
            }
        );
        
    }
}
  • 인수 테스트 진행 시, 필요한 데이터를 먼저 저장하고 조회하는 순서로 테스트 코드를 작성할 경우 가독성이 안 좋아진다.
  • dynamicTest 메서드를 통해서 인수 테스트의 절차를 나눈다면 Test의 이름도 설정하고 가독성이 높아지고 디버깅하기 좋아진다.

 

🐻 Mocito: 모킹, 스파이, 검증 등 기본 정리

🐻 좋은 테스트 코드란

First 규칙

Fast

테스트 코드의 실행 시간이 길어지면 검증하는데 주춤하게 된다. 왜냐하면 프로젝트의 기한을 맞추는 것이 더 중요하고 테스트코드를 실행하는 시간 동안 작업 하는 시간이 줄어들기 때문이다. 그렇기 때문에 테스트 코드는 빠르게 실행되어야 한다. 또한 CI, CD 과정에서 애플리케이션을 빌드하는데 시간이 오래 걸린다면 개발자의 생산성이 저하된다.

 

Islated

객체는 객체 간의 상호작용을 통해서 기능을 제공하지만, Test Code는 독립적이어야 한다. 의존하는 객체에 따라서 실패하거나 성공하면 일관성이 없어진다. 의존성을 끊어내고 결과값에 의존하지 않아야 한다.

 

Repeatable

테스트 코드는 자주 반복적으로 실행된다. 그렇기 때문에 실행할 때마다 동일한 테스트 결과가 나와야 한다.

만약, 테스트 코드가 의존성을 가지고 테스트 결과가 실행할 때마다 상이하다면 동료 개발자에게 혼란을 줄 수있다. 또한 CICD 과정에서 항상 성공하지 않고 간헐적으로 성공하게 된다면 안정성이 저하된다.

 

Self-Validator

테스트 코드는 기댓값과 실제 값을 혼자서 비교하고 판단할 수 있어햐 한다.

로그를 통해서 테스트의 결과를 개발자가 직접 검증하는것과 시스템이 검증하는것과의 차이를 생각해보자. (자동화)

모든 테스트 코드의 로그를 눈으로 확인한다면 시간이 오래걸릴 뿐더러 휴면에러가 발생한다.

 

Timely

 Unit Test는 프로덕션 코드가 테스트를 성공하기 직전에 구성되어야 한다.

테스트 주도 개발(TDD) 방법론에 적합한 원칙이지만 실무에서 적용되지 않는 사례가 더 많다고 한다. 아무래도 TDD로 개발을 하게 된다면 시간에 쫒기게 되어 어려울 것 같다.

하지만 테스트 코드는 반드시 있어야 한다. 오히려 테스트 코드가 없어서 들어가는 리소스 QA, 기능 변경 시마다 QA 필요성. 빠른 반복주기의 이점 등등 때문에.

 

🐻 Test Doubles

대역 Dummy

단위 테스트를 진행하는데 생성하기 까다로운 클래스를 직접 생성해서 테스트하지 않고, 이를 대신해 테스트를 진행을 도와줄 가짜 객체를 Test Doubles라고 말한다.

 

대신 해주는 객체들의 모임

  • Dummy
  • Stub
  • Spy
  • Mock
  • Fake

Test Stub

  • Stub은 테스트 중에 만들어진 호출에 대해 미리 준비된 답변을 제공하는 형태를 말한다.
  • 실제 객체처럼 동작하는 클래스를 직접 구현한다. 테스트에 필요한 구현에 집중하고 부가적인 기능은 구현하지 않는다.

 

Tesy Spy

Spy 객체는 2가지 정도의 정의가 나뉜다.

  1. Test Doubles 관점
    1. Stub의 역할을 하면서 부가적인 정보를 기록하는 기능을 가진 객체를 의미한다.
    2. 호출 횟수를 기록한다.
  2. Mocito 관점
    1. 실제 객체를 감싸 지정된 메서드만 Stub하고 그렇지 않은 메서드는 Production Code 처럼 행동을 하는 객체를 말한다.

 

Test Mock

실제 객체의 동작을 모방하는 객체(Mock Object)를 말한다.

메서드의 호출에 대한 기대를 명세하고 미리 정의해놓은 내용에 따라 동작(or 호출)했는지 검증하기 위해서 사용한다.

 

🐻 Stub vs Mock

Stub : 테스트에 필요한 호출에 대한 답을 미리 정의해 제공하는 객체

  • 실제 객체처럼 동작하는 클래스를 직접 구현.
  • 테스트에 필요한 구현에 집중하고 부가적인 기능은 구현하지 않는다
  • Input에 대한 어떤 Output이 반환되었는지 검증한다.
  • 즉 상태(Value)를 검증한다.

Mock : 예상된 동작을 가진 객체

  • 다양한 Mock Framework를 통해서 Mock 객체를 생성하고 호출에 대한 기대 명세와 기대되는 행동에 명세를 정의한다.
  • Input과 Output에는 관심이 없다. 정해져 있는 비즈니스 로직대로 행동이 되었는지 검증한다.
  • 즉 행동(Behavior)를 검증한다.

 

모호한 정의

MSDN에서는 Test Doubles에 대해서 아래와 같이 이야기한다.

"Test Doubles의 객체들이 이론상 구별되는 것처럼 보이지만, 실제로는 그 차이의 간격이 모호하다.
즉, Test Doubles의 경계는 명확한 것이 아니므로 스펙트럼처럼 바라봐야 바람직하다"

 

🐻 왜 Test Doubles를 사용해야 하는가

이유는 간단하다.  주요 "관심사" 에 집중하기 위함이다. 특정 객체를 테스트하는데, 의존하는 다른 객체에 의해서 테스트하기가 어려워지면 테스트 코드를 작성하기에 어려워진다.

 

🐻 알아두면 유용한 어노테이션

@Mock
private PerformanceService performanceService;

@Mock
private ReservationRepository reservationRepository

Mock 객체를 손쉽게 생성해주는 어노테이션

 

@MockBean
private TicketingService ticketingService;

Spring Context에 Mock 객체를 등록하고 Application Context에 의해 빈을 주입할 때 동작하여 mock 객체를 사용할 수 있도록 빈으로 등록해주는 어노테이션

 

최근에는 @MocitoBean 어노테이션을 사용하도록 변경된것 같다. @MockBean 어노테이션이 디플리케이트 되어있었다.

 

@InjectMocks
private TicketingService ticketingService;

@Mock
private PerformanceService performanceService;

@Mock
private ReservationRepository reservationRepository;

 

클래스가 필요한 의존성과 맞는 Mock 객체들을 감지해서 해당 클래스의 객체가 만들어질 때 Mock 객체를 사용해서 초기화할 수 있도록 도와주는 어노테이션

 

🐻 Spring Boot Test

@SpringBootTest 는 스프링부트의 컨텍스트 영역을 이용하여 테스트를 가능하도록 해주는 어노테이션이다.

빈을 주입하여 테스트를 진행할 수 있도록 해주는 기능을 제공한다. 

 

🐻 통합 테스트

통합 테스트를 제공하는 가장 기본적인 Test Annotation으로 애플리케이션이 실행될 때의 설정을 임의로 바꿀 수 있고 여러 단위 테스트를 하나의 통합 테스트로 수행할 수 있다.

 

빈의 생성범위는 스프링 애플리케이션의 모든 빈을 초기화하고 등록한다.

 

🐻 Properties 설정

@SpringBootTest({"company=nhn-kcp", "it.study=gomgom"})
@SpringBootTest(value = {"...", "..."})
@SpringBootTest(properties={...})

스프링부트 프로퍼티 값을 임시 설정 값을 정의 및 할당할 수 있다.

  • Key = Value 형식으로 선언하면 된다.
  • Depth가 있는 경우 점(Dot)으로 표현하면 된다.

 

🐻 classes 설정

@SpringBootTest(classes={TicketController.class, TicketingService.class})
  • Classes 속성에 선언한 클래스의 빈만 초기화한 후 등록한다
  • 애플리케이션의 규모가 대규모인 경우, 빈이 많아서 테스트를 실행하는데 소요되는 시간이 많이 걸릴 수 있다. 그럴때 classes 설정을 사용해 가볍게 빈을 등록할 수 있다.
  • @Configuration으로 지정한 빈도 등록할 수 있다. 

 

🐻 webEnvironment 설정

@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)
  • 스프링부트의 내장되어 있는 톰캣 서버의 설정을 할 수 있는 속성
    • Options
      • Mock : DEFAULT 설정으로 내장 톰캣이 구동되지 않는다.
      • RANDOM_PORT : 서비스 포트가 랜덤으로 지정되고 상용 애플리케이션에서 구동되는 것처럼 내장 톰캣을 구동
      • DEFINED_PORT : 지정된 포트로 내장 톰캣이 구동

 

🐻 AutoconfigureTestDatabase

@SpringBootTest
@AutoConfigureTestDatabase(
	replace=Replace.ANY,
    connection=EmbeddedDatabaseConnection.H2
)
  • replace
    • NONE: DataSource를 대체하지 않음. 프로파일 설정에 따라 데이터 소스 결정
    • AUTO_CONFIGURE: 필요하다면, 자동 구성된 것으로 대체함
    • ANY : 구성과 상관없이 connection에 선언한 내장 데이터 소스로 대체
  • connection : 데이터베이스 커넥션 속성을 결정하는 설정
    • None
    • H2
    • HSQL
    • DERBY

 

🐻 Transactional

테스트가 모두 완료된 후에, Test 에 사용하며 생성된 데이터를 자동으로 롤백해주는 어노테이션

 

 

🐻 테스트 관련 기술면접 관련 예상 질문

🐻단위 테스트

Q1.테스트 코드 작성 경험의 유무

 

Q2.단위 테스트의 목적

소프트웨어 테스트의 가장 큰 목적은 오류를 처리하는 것보다 저자(개발자)의 의도대로 단위 기능이 작동하는지 확인하는 것이다.
따라서 저자는 정확한 요구사항을 인지 및 이해하는 것도 중요하다. 
그래서 TDD의 경우, 점진적으로 테스트 코드를 작성할 때, 테스트 케이스를 통과시킬 수 있는 만큼만 개발하는 이유가 이러한 이유다.

 

Q3. 단위 테스트의 장점과 단점

단점은 최초에 Test Code를 작성하는데 Resource(시간, 학습)가 많이 소요된다.
장점은 
1. 테스트 코드를 기반으로 클린 코드를 위한 리팩토링을 할 수 있다.
2. 개발자의 불안감을 해소할 수 있다.
3. SOLID 원칙 중 단일 책임 원칙을 강제할 수 있다. (그렇지 않으면 테스트 코드 작성하기가 어려움)
4. 새롭게 추가되는 규칙(로직)에 대한 코드를 추가하고 빠르게 테스트할 수 있다.
5. 반복적인 리소스 QA를 줄일 수 있다.

 

Q4. 테스트 코드 커버리지

코드 커버리지는 테스트 코드가 소스 코드의 얼마나 많은 부분을 실행하고 테스트하는지를 나타내는 지표를 말한다.
구문 커버리지 : 실행된 문장 수 / 전체 문장 수
결정 커버리지 : 실행된 조건문 블록 수 / 전체 조건문 블록 수

 

🐻통합 테스트

Q1. 통합테스트란 ?

요구사항 또는 기획서에서 정의한 온전한 기능을 테스트하는 것을 통합 테스트라고한다.
여기서는 Test Doubles를 사용하지 않고 Production 코드와 환경을 사용해서 테스트하는 것이 단위 테스트와의 차이점이라고 할 수 있다.

 

Q2. 왜 통합 테스트를 해야하나요?

 

Q3. 통합 테스트 시나리오를 어떻게 작성할 수 있나요?

하나의 서비스를 예시를 들어서 통합 테스트 시나리오를 작성한다.
통합 테스트는 단위 테스트를 마친 전체 기능을 테스트한다는 것이고 서로 관련이 있는 기능 간의 테스트 또한 이 시점에 필요하다는 것을 알고 시나리오를 천천히 설명한다.
예 : 음료 자판기에 탑재된 소프트웨어를 제작했다고 가정하고 작성한 소프트웨어의 통합 테스트 시나리오를 말하기

 

🐻성능 테스트

Q1. 성능테스트란

신규 서비스나 현재 운영중인 서비스에 특별한 이벤트로 대량의 트래픽이 예상되는 경우 애플리케이션에 부하 또는 지연 스트레스가 발생했을 때 정상 동작 및 상태를 확인하기 위해서 진행한다.
우리가 예상한 요청의 수를 적절하게 서버가 처리할 수 있는지를 테스트한다.

 

Q2. 성능 테스트의 목적

1. 성능 테스트를 통해서 병목 현상이나 단위 및 통합 테스트 때 경험하지 못했거나 발견하지 못한 부분을 찾아서 미래에 발생할 수 있는 이슈를 사전에 확인하고 통제하기 위함
2. 우리의 서비스가 클라이언트 요청을 얼마나 빠르고 정확하게 오류없이 정상적으로 처리하고 응답할 수 있는지를 확인하기 위함이다.

 

Q3. 사용해본 성능 테스트 툴

JMeter, nGrinder, Locust 등 사용 여부, 실제 테스트해본 경험 여부 만들기

 

🐻테스트 도구 및 프레임워크

Q1. Test Doubles 란?

단위 테스트를 하기 위해서 테스트 코드를 작성하는데 관심사에서 벗어난 문제를 해결하기 위함
이러한 문제를 해결하기 위해서 나온 해결책이 대역 테스트 더블이다.

실제 빈이 올라가지 않아 가벼운 테스트를 실행하게 되어 실행 시간이 줄어든다.

 

Q2. Mock Stub 차이점

Stub은 Production Code를 단위 테스트의 시나리오에 맞게 필요한 기능만 동작하는 것처럼 구현한 객체를 말한다.
Stub을 이용해서 현재 테스트 중인 코드의 실제값과 예상값을 비교 및 검증이 필요한 상태 검증 테스트에 주로 사용한다.

Mock은 메서드의 호출을 기대하는 명세와 함께 반환되는 값을 정의한다. 테스트 코드에서 Mock에서 반환한 값을 이용해서 연산을 하기보다는 특정 메서드에 대한 호출 여부를 검증하는 행위 검증 테스

 

Q3. Mocito란

테스트 더블을 구현한 프레임워크를 말하며, Java 진영에서 Mock을 사용하기 위해서 사용되는 대표적인 프레임워크다.
Java 테스트를 통해 Mock 객체를 쉽게 생성하고 관리 검증할 수 있는 방법을 제공한다.

 

Q4. Mock을 사용해서 단위테스트를 작성해야 하는 이유

테스트 더블, 테스트하려는 객체 또는 메서드가 다른 객체에 의존하고 있고, 해결하기 위해서 테스트 환경을 구성해야 하는데 리소스 문제
현재 진행하고 있는 테스트의 주요 관심사에만 초점을 맞춰서 빠르게 테스트하기 위해서

 

 

🐻TDD, BDD

Q1. TDD란

전통적인 개발 방식에서 벗어나, 기능 정의서를 기반으로 Test Case 목록을 작성하고 작성된 목록을 기준으로 하나씩 테스트 코드를 작성해 나가면서 점진적으로 완성시켜가는 개발 방법론을 말한다.

 

Q2. TDD 장점 단점

장점
1. 점진적으로 기능을 완성시켜 나가기 때문에 요구사항을 누락시킬 수 있는 확률을 낮춘다.
2. 다양한 테스트 코드로 완성된 코드로 배포가 나가기 때문에 개발자의 불안함을 낮출 수 있는 요소를 가진다.
3. 기능 정의에 대한 명세가 테스트 코드로 상세하게 되어있고 문서화된 API 문서보다 더 빠르고 쉽게 기능에 대한 정의를 이해하고 직접 테스트 코드를 실행해서 결과를 확인할 수 있다.

단점
1. 점진적으로 개발을 하다보니, 이렇게 까지 쪼개서 개발을 해야하나? 라는 반감. 
2. 전통적인 개발 방식보다 더 많은 리소스 (시간, 학습)가 투입된다.

그래서 무조건 TDD 방식이 좋다고 말하기는 어렵다. 자신이 속해 있는 개발팀 또는 기업의 비즈니스 속도에 맞는 적절한 개발 방식을 선택해야 한다. 그러나 탄탄한 서비스를 위해서 단위 테스트 코드를 작성하는 것은 선택이 아니라 필수 조건이라 생각한다. 

 

Q3. BDD란

BDD는 TDD에서 파생된 기법으로 사용자의 행위를 먼저 검증하고 이것을 바탕으로 테스트와 개발을 진행하는 방법을 말한다.
어떤 특정 환경이 주어졌을 때, 어떤 행위를 하였고, 그 결과가 동일한지 검증하는 개발 방법론을 말한다.