ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트 주도 개발 시작하기 - chapter5 ~ chapter7
    Test/테스트주도개발 시작하기 (최범균) 2023. 9. 30. 21:11

    5.  JUnit5 기초

    5-1. JUnit 모듈 구성

    - JUnit5의 구성 요소

        ㆍJUnit 플랫폼 : 테스팅 프레임워크를 구동하기 위한 런처와 테스트 엔진을 위한 API 제공

        ㆍJUnit 주피터(Jupiter) : JUnit5를 위한 테스트 API와 실행 엔진을 제공한다.

        ㆍJUnit 빈티지(Vintage) : JUnit3과 4로 작성된 테스트를 JUnit5 플랫폼에서 실행하기 위한 모듈을 제공한다.

     

    5-2. @Test 애노테이션과 테스트 메서드

    - JUnit 코드의 기본 구조는 간단하다. 테스트로 사용할 클래스를 만들고 @Test 애노테이션을 메서드에 붙이기만 하면 된다.

    - JUnit의 Assertions 클래스는 검증하기 위한 목적의 다양한 정적 메서드를 제공한다.

     

    5-3. 주요 단언 메서드

    - Assertions 클래스가 제공하는 주요 단언 메서드

    메서드 설명
    assertEquals(expected, actual) 실제 값(actual)이 기대하는 값(expected)과 같은지 검사한다.
    assertNotEquals(unexpected, actual) 실제 값(actual)이 특정 값(unexpected)과 같지 않은지 검사한다.
    assertSame(Object expected, Object actual) 두 객체가 동일한 객체인지 검사한다.
    assertNotSame(Object unexpected, Object actual) 두 객체가 동일하지 않은 객체인지 검사한다.
    assertTrue(boolean condition) 값이 true인지 검사한다.
    assertFalse(boolean condition) 값이 false인지 검사한다.
    assertNull(Object actual) 값이 null인지 검사한다.
    assertNotNull(Object actual) 값이 null이 아닌지 검사한다.
    fail() 테스트를 실패처리한다.

     

    - Assertions가 제공하는 익셉션 발생 유무 검사 메서드

    메서드 설명
    assertThrows(Class<T> expectedType,
                                 Executable executable)
    executable을 실행한 결과로 지정한 타입의 익셉션이 발생하는지 검사한다.
    assertDoesNotThrow(Executable executable) executable을 실행한 결과로 익셉션이 발생하지 않는지 검사한다.
    IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
        AuthService authService = new AuthService();
        authService.authenticate(null, null);
    });
    assertTrue(thrown.getMessage().contains("id"));

     

    - 모든 검증을 실행하고 그 중에 실패한 것이 있는지 확인하고 싶을 때

    assertEquals(3, 5/2);	// 검증 실패로 에러 발생
    assertEquals(4, 2*2);	// 이 코드는 실행되지 않음
    assertAll(
        () -> assertEquals(3, 5/2);
        () -> assertEquals(4, 2*2);
        () -> assertEquals(6, 11/2);
    )

     

    5-4. 테스트 라이프 사이클

    @BeforeEach 애노테이션과 @AfterEach 애노테이션

    - JUnit은 각 테스트 메서드마다 다음 순서대로 코드를 실행

        ㆍ1. 테스트 메서드를 포함한 객체 생성

        ㆍ2. (존재하면) @BeforeEach 애노테이션이 붙은 메서드 실행

        ㆍ3. @Test 애노테이션이 붙은 메서드 실행

        ㆍ4. (존재하면) @AfterEach 애노테이션이 붙은 메서드 실행

     

    @BeforeAll 애노테이션과 @AfterAll 애노테이션

    - 한 클래스의 모든 클래스 메서드가 실행되기 전에 특정 작업을 수행한다면 @BeforeAll 애노테이션 사용

    - @BeforeAll 애노테이션은 정적 메서드를 붙이는데 이 메서드는 클래스의 모든 테스트 메서드를 실행하기 전에 한 번 실행된다.

    - @AfterAll 애노테이션은 반대로 클래스의 모든 테스트 메서드를 실행한 뒤에 실행된다.

    - 이 메서드 역시 정적 메서드에 적용한다.

     

    5-5. 테스트 메서드 간 실행 순서 의존과 필드 공유하지 않기

    - 테스트 메서드가 특정 순서대로 실행된다는 가정하에 테스트 메서드를 작성하면 안된다.

    - 각 테스트 메서드는 서로 독립적으로 동작해야 한다.

     

    5-6. 추가 애노테이션: @DisplayName, @Disabled

    - @DisplayName 애노테이션을 사용해서 테스트에 표시 이름을 붙일 수 있다.

    - JUnit은 @Disabled 애노테이션이 붙은 클래스나 메서드는 테스트 실행 대상에서 제외한다.

     

    5-7. 모든 테스트 실행하기

    - mvn test (래퍼를 사용하는 경우 mvnw test) & mvn package 명령어

    - gradle test (래퍼를 사용하는 경우 gradlew test) & gradle build 명령어

    - 인텔리제이는 src/test/java 폴더에서 테스트 실행


    6. 테스트 코드의 구성

    6-1. 기능에서의 상황

    - 어떤 상황이 실행 결과에 영향을 줄 수 있는지 찾기 위해 노력한다.

    - 다양한 예외 상황을 찾아내고 이를 코드에 반영해야 기능이 비정상적으로 동작하는 것을 막을 수 있다.

     

    6-2. 테스트 코드의 구성 요소: 상황, 실행, 결과 확인

    - 어떤 상황이 주어지고, 그 상황에서 기능을 실행하고, 실행한 결과를 확인하는 세 가지가 테스트 코드의 기본 골격을 이루게 된다.

        ㆍ상황, 실행, 결과 확인은 영어 표현 given when, then 에 대응한다.

     

    6-3. 외부 상황과 외부 결과

    외부 상태가 테스트 결과에 영향을 주지 않게 하기

    - 외부 상태에 따라 테스트의 성공 여부가 바뀌지 않으려면 테스트 실행 전에 외부를 원하는 상태로 만들거나 테스트 실행 후에 외부 상태로 원래대로 되돌려 놓아야 한다.

     

    외부 상태와 테스트 어려움

    - 상황과 결과에 영향을 주는 외부 요인은 파일, DBMS, 외부 서버 등 다양하다.

    - 테스트 대상의 상황과 결과에 외부 요인이 관여할 경우 대역을 사용하면 테스트 작성이 쉬워진다.

        ㆍ대역은 테스트 대상이 의존하는 대상의 실제 구현을 대신하는 구현인데 이 대역을 통해서 외부 상황이나 결과를 대체할 수 있다.

     


    7. 대역

    7-1. 대역의 필요성

    - 테스트를 작성하다 보면 외부 요인이 필요한 시점이 있다.

        ㆍ테스트 대상에서 파일 시스템을 사용

        ㆍ테스트 대상에서 DB로부터 데이터를 조회하거나 데이터를 추가

        ㆍ테스트 대상에서 외부의 HTTP 서버와 통신

    - 대역에는 종류는 스텁, 가짜, 스파이, 모의 객체가 존재하는데 각 대역 종류마다 쓰임새가 다르다.

     

    7-2. 대역을 이용한 테스트

     

     

     

    7-3. 대역을 사용한 외부 상황 흉내와 결과 검증

     

    7-4. 대역의 종류

    대역 종류 설명
    스텁(Stub) 구현을 단순한 것으로 대체한다. 테스트에 맞게 단순히 원하는 동작을 수행한다. StubCardNumberValidator가 스텁 대약에 해당한다.
    가짜(Fake) 제품에는 적합하지 않지만, 실제 동작하는 구현을 제공한다. DB 대신에 메모리를 이용해서 구현한 MemoryAutoDebitInfoRepository가 가짜 대역에 해당한다.
    스파이(Spy) 호출된 내역을 기록한다. 기록한 내용은 테스트 결과를 검증할 때 사용한다. 스텁이기도 하다
    모의(Mock) 기대한 대로 상호작용하는지 행위를 검증한다. 기대한 대로 동작하지 않으면 익셉션을 발생할 수 있다. 모의 객체는 스텁이자 스파이도 된다

     

     

    7-5. 상황과 결과 확인을 위한 협업 대상(의존) 도출과 대역 사용

     

    7-6. 대역과 개발 속도

    - 대역을 사용하면 실제 구현이 없어도 다양한 상황에 대해 테스트할 수 있다.

     

    7-7. 모의 객체를 과하게 사용하지 않기

    - 모의 객체를 이용하면 대역 클래스를 만들지 않아도 되니깐 처음에는 편할 수 있다.

        ㆍ하지만 결과 값을 확인하는 수단으로 모의 객체를 사용하기 시작하면 결과 검증 코드가 길어지고 복잡해진다.

    - 특히 DAO나 리포지토리와 같이 저장소에 대한 대역은 모의 객체를 사용하는 것보다 메모리를 이용한 가짜 구현을 사용하는 것이 테스트 코드 관리에 유리하다.

    - 물론 처음에는 가짜 대역을 구현해야 하니깐 귀찮을 수 있는데 일단 가짜 대역을 구현하면 모의 객체를 사용할 때보다 테스트 코드가 간결해지고 관리하기 쉬워진다.

    댓글

Designed by Tistory.