[첫번째 프로젝트] 13.[7-1] JUnit 5 Exercise

DAEILLIM·2024년 1월 24일
0

첫번째 프로젝트

목록 보기
13/17
post-thumbnail

JUnit 5 Exercise

1. JUnit 5 사용 예시 -1 : 라이프사이클 테스트

  • JUnit의 사용예시를 통해서 각각 어노테이션과 메서드의 사용법을 알아봅니다.

1.1 @Test

  • JUnit에서 메서드를 테스트 메소드로 표시하는 데 사용됩니다.
  • 테스트 클래스가 실행되면 JUnit은 @Test로 주석이 달린 메소드를 개별적인 테스트 케이스로 식별하여 실행합니다.

1.2 @Test 수행구조

  • 테스트를 시작하면 @Test 어노테이션을 통해 testSum()이라는 함수를 수행시키고 종료합니다.

image-20240124223121817

1.3 @Test 예제1

  • 해당 테스트에서는 두 숫자의 합이 맞는지 여부를 확인하는 테스트를 구성하였습니다.
  • a + b의 값과 expectedSum값을 비교했을 때 해당 결과값이 맞는지에 대해 확인합니다.
package com.adjh.multiflexapi;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * 메인 테스트
 *
 * @author : lee
 * @fileName : MultiFlexApiApplicationTests
 * @since : 12/6/23
 */
class MultiFlexApiApplicationTests {

    @Test
    @DisplayName("두 숫자의 합 테스트")
    void testSum() {
        int a = 5;
        int b = 7;
        int expectedSum = 12;
        int actualSum = a + b;
        Assertions.assertEquals(expectedSum, actualSum);
    }
}

해당 값이 맞는 경우는 ‘테스트 통과’ 메시지를 출력합니다.

img

해당 값이 틀린 경우는 ‘테스트 실패’ 메시지를 출력합니다.(a=5, b=1 이기 때문에 실패가 출력된 것입니다.)

img

2. 클래스/테스트 전후 수행 테스트 : @BeforeAll, @AfterAll, @BeforeEach, @AfterEach

  • @BeforeAll, @AfterAll 어노테이션을 이용하여 테스트 클래스의 시작/후에 단 한번 호출이 됩니다.

  • @BeforeEach, @AfterEach 어노테이션을 이용하여 @Test 마다 시작/후에 호출이 됩니다.

어노테이션설명
@BeforeAllJUnit 테스트 클래스 내에서 모든 테스트 메소드 실행 전에 한 번 실행되는 메소드를 정의하는 데 사용됩니다.
보통 테스트 환경의 설정이나 초기화에 사용됩니다.
@AfterAllJUnit 테스트 클래스 내에서 모든 테스트 메소드 실행 후에 한 번 실행되는 메소드를 정의하는 데 사용됩니다.
주로 사용한 리소스를 정리하거나 테스트 환경을 종료하는 데 사용됩니다.
@BeforeEachJUnit 테스트 클래스 내의 각 테스트 메소드마다 실행되는 메소드를 정의하는 데 사용됩니다.
주로 각 테스트의 준비 작업이나 초기화에 사용됩니다.
@AfterEachJUnit 테스트 클래스 내의 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 데 사용됩니다.
주로 각 테스트의 후처리 작업이나 정리 작업에 사용됩니다.

2.1 테스트 수행구조

  1. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  2. @Test 어노테이션을 통해 수행되기 이전에 @BeforEachsetUp() 함수가 수행됩니다.
  3. @Test 어노테이션을 통해 testEqualsSum() 함수가 수행됩니다.
  4. @Test 어노테이션을 통해 수행된 후 @AfterEachcleanUp() 함수가 수행됩니다.
  5. 동일한 과정으로 @Test 어노테이션을 통해 수행되기 이전에 @BeforEachsetUp() 함수가 수행됩니다.
  6. 동일한 과정으로 @Test 어노테이션을 통해 testIsNotNull() 함수가 수행됩니다.
  7. 동일한 과정으로 @Test 어노테이션을 통해 수행된 후 @AfterEachcleanUp() 함수가 수행됩니다.
  8. 최종 테스트 종료 이전에 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.
image-20240124224722909

2.2 테스트 코드

package com.adjh.multiflexapi;

import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 메인 테스트
 *
 * @author : lim
 * @fileName : MultiFlexApiApplicationTests
 * @since : 01/24/24
 */
class MultiFlexApiApplicationTests {

    @Test
    @DisplayName("두 숫자의 합 테스트")
    void testEqualsSum() {
        System.out.println("[+] @test assertEquals Annotation");
        int a = 5;
        int b = 7;
        int expectedSum = 12;
        int actualSum = a + b;
        Assertions.assertEquals(expectedSum, actualSum);
    }

    @Test
    @DisplayName("Object NULL 여부 테스트")
    void testIsNotNull() {
        Object obj = null;
        Assertions.assertNull(obj);
        System.out.println("[+] @test assertNull Annotation");
    }

    @BeforeEach // 각 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션
    void setUp() {
        System.out.println("@BeforeEach Annotation");
    }

    @AfterEach // 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션
    void cleanUp() {
        System.out.println("@AfterEach Annotation");
    }

    @BeforeAll // 클래스 내의 모든 테스트 메서드 실행 전에 한 번 호출되는 메서드
    static void setUpBeforeClass() {
        System.out.println("@BeforeAll Annotation");
    }

    @AfterAll // 클래스 내의 모든 테스트 메서드 실행 후에 한 번 호출되는 메서드
    static void cleanUpAfterClass() {
        System.out.println("@AfterAll Annotation");
    }
}

수행 결과

  • @Test로 구성한 두 개의 메서드가 성공적으로 수행되었습니다.
  • @BeforeAll, @AfterAll의 경우는 테스트의 시작 전후에 한 번씩만 수행되었고 @BeforeEach, @AfterEach의 경우는 @Test로 구성한 메서드의 전후에 각각 수행이 되었습니다.
img

3. Given-when-then 패턴 시나리오 테스트하기

  • @Test를 선언한 메서드에서 시나리오를 구성하기 위해 Given-When-Then 패턴을 통하여서 시나리오를 구성합니다.
섹션설명
Given : 설정테스트의 초기 상태 또는 사전 조건을 설정합니다. 입력 데이터나 테스트가 실행될 문맥을 지정합니다.
When : 동작테스트되는 동작 또는 이벤트를 설명합니다. 테스트되는 특정 메서드나 동작을 나타냅니다.
Then : 검증"When" 섹션에서 설명한 동작으로 인해 기대되는 결과 또는 동작을 정의합니다.

3.1 Given-when-then 수행 구조

  1. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  2. @Test 어노테이션을 통해 testAddingTwoNumbers() 함수가 수행됩니다.
  3. @Test 어노테이션을 통해 testDividingTwoNumbers() 함수가 수행됩니다.
  4. 테스트를 종료하면서 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.
image-20240124225503699

3.2 Given-when-then 예제1

package com.adjh.multiflexapi;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

/**
 * 중첩 테스트 클래스
 *
 * @author : lim
 * @fileName : CalculatorTests
 * @since : 01/24/24
 */
class CalculatorTests {

    @BeforeAll
    static void setUpBeforeAll() {
        System.out.println("@BeforeAll Annotation");
    }

    @Test
    void testAddingTwoNumbers() {
        // given : 초기 상태 지정
        int a = 2;
        int b = 3;

        // when : 테스트 동작 정의
        int result = Calculator.add(a, b);

        // then : 검증
        int expected = 5;
        Assertions.assertEquals(expected, result);
        System.out.println("Test for adding two numbers passed");
    }

    @Test
    void testDividingTwoNumbers() {
        // given : 초기 상태 지정
        int a = 10;
        int b = 2;

        // when : 테스트 동작 정의
        int result = Calculator.divide(a, b);

        // then : 검증
        int expected = 5;
        Assertions.assertEquals(expected, result);
        System.out.println("Test for dividing two numbers passed");
    }

    @AfterAll
    static void cleanUpAfterAll() {
        System.out.println("@AfterAll Annotation");
    }
}

class Calculator {
    static int add(int a, int b) {
        return a + b;
    }

    static int divide(int a, int b) {
        return a / b;
    }
}

수행 결과

  • 시작 전후에 @BeforAll, @AfterAll로 지정한 메서드가 실행이 되었습니다.
  • @Test로 지정한 testAddingTwoNumbers(), testDividingTwoNumbers() 함수가 실행이 되었습니다.
img

4. 중첩된 테스트

  • 클래스 내에서 테스트가 많이 있어서 가독성이 떨어지는 경우 클래스 내에 클래스를 구현하여서 @Nested 어노테이션을 통해서 테스트 별로 묶어서 수행하는 방법입니다.
  • 이를 통해 테스트들을 구조화하여 가독성을 향상하는데 도움을 줍니다·

4.1 @Nested

JUnit에서 중첩된 테스트 클래스를 작성하는 데 사용됩니다. 중첩된 테스트 클래스는 특정 테스트 그룹을 더 잘 구조화하고 테스트 코드의 가독성을 향상시키는 데 도움이 됩니다.
- 중첩된 테스트 클래스는 내부 클래스로 선언되며, 외부 테스트 클래스의 인스턴스와 상호작용할 수 있습니다. 이를 통해 외부 테스트 클래스의 상태를 공유하거나 테스트 케이스 간의 의존성을 관리할 수 있습니다

4.2 테스트 수행구조

@Nested 어노테이션이나 @Test 어노테이션은 순서를 보장하지 않기에 구성한 예시로 확인해 주시면 좋을 것 같습니다.

  1. 테스트를 시작하면 @Nested 어노테이션을 통해 CalcTests Class가 수행이 됩니다.
  2. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testSum() 메서드가 수행이 됩니다.
  3. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testAddingTwoNumbers() 메서드가 수행이 됩니다.
  4. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testDividingTwoNumbers() 메서드가 수행이 됩니다.
  5. 다음으로 @Nested 어노테이션을 통해 IsNotNullTests Class가 수행이 됩니다.
  6. IsNotNullTests 클래스 내에서 testsNotNull() 메서드가 수행됩니다.
  7. 테스트가 종료됩니다.
image-20240124230612152

4.3 @Nested 예제1

package com.adjh.multiflexapi;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * 중첩화 된 테스트
 *
 * @author : lim
 * @fileName : NestedTests
 * @since : 01/24/24
 */
class NestedTests {

    @Nested
    class CalcTests {
        @Test
        @DisplayName("두 숫자의 합 테스트")
        void testSum() {
            int a = 5;
            int b = 7;
            int expectedSum = 12;
            int actualSum = a + b;
            Assertions.assertEquals(expectedSum, actualSum);
        }

        @DisplayName("두 숫자의 합 테스트")
        @Test
        void testAddingTwoNumbers() {
            // given : 초기 상태 지정
            int a = 2;
            int b = 3;

            // when : 테스트 동작 정의
            int result = Calculator.add(a, b);

            // then : 검증
            int expected = 5;
            Assertions.assertEquals(expected, result);
            System.out.println("Test for adding two numbers passed");
        }

        @Test
        @DisplayName("두 수의 값 나누기 ")
        void testDividingTwoNumbers() {
            // given : 초기 상태 지정
            int a = 10;
            int b = 2;

            // when : 테스트 동작 정의
            int result = Calculator.divide(a, b);

            // then : 검증
            int expected = 5;
            Assertions.assertEquals(expected, result);
            System.out.println("Test for dividing two numbers passed");
        }
    }

    @Nested
    class IsNotNullTest {
        @Test
        @DisplayName("Object NULL 여부 테스트")
        void testIsNotNull() {
            Object obj = null;
            Assertions.assertNull(obj);
            System.out.println("[+] @test assertNull Annotation");
        }
    }
}

수행 결과

  • NestedTests 클래스 안에 CalcTestsIsNotNullTest 클래스가 존재하는데, 각각 모두 수행이 됨을 확인하였습니다.
img

5. JUnit 5 사용 예시 -2: 서비스 테스트

5.1 @SpringBootTest 어노테이션을 사용하여 테스트 : @SpringBootTest

@SpringBootTest 어노테이션을 사용하여 테스트

  • @SpringBootTest를 사용하면 애플리케이션 컨텍스트를 로딩하고 필요한 빈을 초기화하는 등 테스트에 필요한 설정과 의존성을 설정하는데 도움이 됩니다.

@SpringBootTest

  • 애플리케이션의 전체 컨텍스트를 로드하여 테스트할 수 있습니다. 이를 통해 실제 환경과 유사한 설정에서 테스트를 수행하고 각각의 컴포넌트가 상호작용하는 방식을 확인할 수 있습니다.
  • 애플리케이션의 다양한 컴포넌트를 테스트할 수 있으며, 외부 서비스와의 상호작용, 데이터베이스 연동 등을 포함한 실제 환경에서의 동작을 확인할 수 있습니다.

클래스에 @SpringBootTest 어노테이션을 선언하면 @Test을 수행하면서 아래와 같이 로컬 서버가 수행되면서 테스트가 진행되는 것을 확인할 수 있습니다.

img

5.2 테스트 수행 구조

  1. 클래스 내에 @SpringBootTest 어노테이션을 선언하였습니다. (*이는 @Test 어노테이션 수행직전에 수행이 됩니다)
  2. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  3. @Test 어노테이션이 수행되기 직전에 로컬 서버가 실행이 되어 각각의 컨텍스트를 로드해 옵니다.
  4. @Test 어노테이션을 통해 testIsNotNullCodeList() 함수가 수행됩니다.
  5. 테스트를 종료하면서 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.
image-20240124231458084

5.3 @SpringBootTest 예제1

  • 해당 코드에서는 CodeService를 주입받아서 값을 구성하여 서비스를 호출하는 테스트를 구성하였습니다.
package com.adjh.multiflexapi;

import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * Please explain the class!!
 *
 * @author : lim
 * @fileName : ServiceTests
 * @since : 01/24/24
 */
@SpringBootTest
class ServiceTests {
    @Autowired
    private CodeService codeService;

    @Test
    @DisplayName("코드리스트 테스트 ")
    void codeList() {
        System.out.println("[+] 코드리스트를 조회합니다.");

        // Given
        CodeDto codeDto = CodeDto.builder().cd("java").build();

        // When
        CodeDto resultDto = codeService.selectCode(codeDto);

        // Then
        Assertions.assertNotNull(resultDto);
    }
}

수행 결과

img img

5.4 기존에 구성된 서비스 테스트

  • 기존에 구성한 클래스나 인터페이스를 테스트하기 위해서 모두 다 서비스를 구성할 필요 없이 간단하게 구성이 가능합니다.

클래스/인터페이스 파일명에 오른쪽 마우스키 - Go To - Test 선택

  • 클래스 파일명 또는 인터페이스에 오른쪽 마우스키를 누르고 Go To - Test를 선택합니다.
img

출력된 화면을 확인합니다.

img
메뉴설명
Testing library이는 프로젝트에서 사용되는 테스팅 프레임워크나 라이브러리를 의미합니다.
예를 들어 JUnit이나 TestNG와 같은 것들이 있습니다.
Class Name테스트를 생성하려는 클래스의 이름입니다.
Superclass생성된 테스트 클래스가 위치할 패키지입니다.
(따로 수행하지 않으면 test 폴더에 패키지 명을 가지와서 생성이 됩니다)
Destination package버튼은 입력한 정보를 기반으로 테스트 클래스를 생성합니다.
- @setUp/@Before : 매번 @Test 수행 전에 수행될 메서드 사용여부
- @tearDown/ @After : 매번 @Test 수행 이후에 수행될 메서드 사용여부
Generate입력한 정보를 기반으로 테스트 클래스를 생성합니다.
Generate test method for클래스의 특정 메소드에 대한 테스트 메소드를 생성할 수 있도록 합니다.

아래와 같이 선택하여 테스트 파일을 만듭니다.

img

아래 경로에 파일이 생성되었습니다.

img img

만약 메서드를 추가하여 테스트를 하고 싶으면 Go To - Test를 선택하여 추가할 수도 있습니다.

img

테스트가 잘되었음을 확인하였습니다.

img
profile
필기하고, 타이핑하고, 말하면서 읽고, 코딩하고, 눈으로 읽고 오감으로 공부하기

0개의 댓글