반응형

↓↓↓ 이전 내용

2023.12.30 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (6) - JUnit TEST 기본 설정 및 repository 계층 test 예제

 

[REST API] Spring Boot로 REST API CRUD 간단 구현 (6) - JUnit TEST 기본 설정 및 repository 계층 test 예제

↓↓↓ 이전 내용 2023.12.15 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (5)-사용자 정의 ResponseEntity [REST API] Spring Boot로 REST API CRUD 간단 구현 (5)-사용자 정의 ResponseEntity 이전 내

im-gonna.tistory.com

 

지난 시간 repository 계층의 test를 수행했다.

이번에는 service 계층의 test를 수행하겠다.

 

💡 Service layer test 클래스 자동 생성하기

 

직접 디렉토리를 생성하고 test 클래스를 작성해도 되지만, service 클래스에서 "create test" 버튼 하나로 test 클래스를 바로 생성할 수 있다.

 

create test를 누르면 다음과 같은 창이 뜬다.

 

JUnit5는 기본으로 설정되어 있으며, test class 이름도 알아서 지정해 주는데, 적절해 보이므로 그대로 사용할 것이다.

마찬가지로 설정/해제 메서드를 추가해준다. setUp과 tearDown에 해당한다.

그리고 class의 모든 메서드에 대한 단위 test를 위해 모두 ✔ 하여 추가해준다.

 

 

OK버튼을 누르면, 위와같이 test 디렉토리 하에 repository와 병렬하게 service 단이 생성되었고, 그 안에 test 클래스도 잘 생성되었다.

 

💡 CloudVendorService test 코드 작성하기

 

클래스를 들여다 보면, jUnit Jupiter assertion이 import되어 있는데, 우리는 저번 시간과 동일하게 기본 assertion을 사용할 것이기 때문에 이 부분은(이미 주석처리 되어있지만) 지워준다.

 

우리는 arg.com에서 제공하는 assertion 라이브러리를 사용할 것이다.

 

 

  • Mock 객체 생성하기

 

서비스 계층이 데이터베이스와 직접적으로 통신해서는 안되기 때문에, 데이터베이스 응답을 얻으려면 repository layer를 moking해야 한다. 이 말은 repository 클래스의 객체를 mock object (모의 객체)로서 생성하여 사용함으로써 데이터를 사용한다는 것이다.

 

가상 객체라고 생각하면 된다.

 

따라서 CloudVendorRepository의 경우 mock 객체로서 사용한다.

 

autoCloseable 객체를 사용하는 이유는 리소스를 자동 해제하는 기능으로서 자원을 안전하게 사용하기 위함이다.

 

 

  • 설정 메서드 setUp()

 

클래스 실행 시 전체 클래스에 대한 mock이 자동으로 열리도록 한다. 이는 Mockito의 openMocks 메서드를 사용한다.

그리고 repository test에서와 동일하게, service 객체와 cloudVendor 객체를 생성해준다.

 

  • 해제 메서드

해제 시에는 autoCloseable을 close해주면 된다.

 

  • create 메서드에 대한 test

  • cloudvendor와 repository에 대해서 mock 객체를 선언해준다.
  • when 메서드를 사용해서 cloudvendorrepository의 save메서드를 호출하였을 때 cloudvendor를 return하도록 하여, 저장이 성공한 것처럼 보이도록 설정한다.
  • asserthat 메서드를 통해 실제 service 클래스의 create 메서드를 호출한 결과가 Success와 같은지 확인한다.
  • 결과가 true이면 test가 통과된다. 

createCloudVendor()의 unit test 결과

 

 

  • update 메서드에 대한 test

    • update 메서드는 앞선 create와 유사하므로 실제 service 클래스에서 호출하는 메서드를 update로만 바꾸어 주면 된다.
    • 이 역시 호출결과가 Success를 반환하면 test 통과이다.

updateCloudVendor()의 unit test 결과

 

  • get 메서드에 대한 test

  • mock 객체를 생성한다.
  • when 메서드를 사용해서 cloudvendorrepository의 findById메서드를 호출하였을 때, cloudvendor를 return하도록 하는데, 이때 find결과가 있을수도 없을 수도 있으므로 optional로 nullable을 설정해준다.
  • asserthat 메서드를 통해 실제 service 클래스의 get 메서드를 호출하여 그 cloudvendor의 name과 클래스에서 선언한 cloudvendor의 name이 동일한지 확인한다.
  • 결과가 true이면 테스트는 통과이다.

getCloudVendor()의 unit test 결과

 

  • get all 메서드에 대한 test

  • 앞선 get메서드와 로직은 비슷하나, all 이므로 list단위로 반환한다는 차이가 있다.
  • 따라서 리스트 중 첫번째 cloudVendor의 phoneNumber를 비교하도록 한다.
  • 결과로 true를 반환하면 test는 통과이다.

getAllCloudVendor()의 unit test 결과

 

  • delete 메서드에 대한 test

  • 이 메서드의 test에서는 mock을 생성할 때, 실제로 repository 클래스의 메서드를 사용할 수 있도록 설정해준다.
  • 이를 위해서 Mokito의 CALLS_REAL_METHODS를 인자로 함께 넣어준다.
  • 또한 doAnswer를 사용해, cloudVendorRepository가 호출되었을 때 실제로 Method를 사용하도록 하고 deleteById 메서드를 호출하도록 한다.
  • asserThat으로 service의 delete메서드를 수행하고 나서 Success를 반환해야 한다.
  • 반환한다면 test는 통과한다.

deleteCloudVendor()의 unit test 결과

 

service test 클래스의 test 실행 결과 모두 통과하는 것을 확인하였다.

service 단의 모든 unit test가 성공적

 

 

test시에는 내 코드가 실제로 구현한 모든 코드를 test할 수 있는지 확인할 필요가 있다.

보통 90%이상이면 옳다고 판단한다.

IntelliJ에서는 아래와 같이 test coverage를 확인할 수 있도록 기능을 제공하고 있다. 

test coverage 확인 결과 91%

 

service class의 test coverage 확인 결과 91%가 cover되는 것을 확인하였다.

그럼 어느 부분을 cover하지 못했을까?

실제 service class의 코드를 살펴보자.

 

각 메서드마다 test가 cover된 부분은 왼쪽에 초록색 바로 표시가 된다. 

내리다 보니, getCloudVendor 메서드 쪽에서 중간에 빨간색 바를 확인하였다.

 

왼쪽을 보면 throw~ 예외처리 부분에 대한 test의 범위는 빨간색으로 표시되면서 포함되지 않은 것을 확인할 수 있다.

 

이렇게 test covergae를 통해서 내가 짠 test 코드가 실제로 구현의 어느 부분까지 cover할 수 있는지 확인 가능하다.

 

 

오늘은 service layer의 test code를 구현해보았다.

 

다음 시간에는 Controller의 test를 구현해보자.

 

↓↓↓ 다음 내용

2024.01.10 - [Web Application/Backend] - [REST API] Spring Boot로 REST API 프로젝트 (8) - JUnit TEST controller 계층 test 예제

 

[REST API] Spring Boot로 REST API 프로젝트 (8) - JUnit TEST controller 계층 test 예제

↓↓↓ 이전내용 2024.01.05 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (7) - JUnit TEST service 계층 test 예제 [REST API] Spring Boot로 REST API CRUD 간단 구현 (7) - JUnit TEST service 계층 test 예

im-gonna.tistory.com

 

반응형
반응형

 

↓↓↓ 이전 내용

2023.12.15 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (5)-사용자 정의 ResponseEntity

 

[REST API] Spring Boot로 REST API CRUD 간단 구현 (5)-사용자 정의 ResponseEntity

이전 내용↓↓↓ 2023.11.22 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (4)-예외처리/handle Exception [REST API] Spring Boot로 REST API CRUD 간단 구현 (4)-예외처리/handle Exception 이전 내용↓

im-gonna.tistory.com

💡 JUnit test 기본 설정

 

작성한 코드가 백에서 잘 동작하는 지를 확인하기 위해 TEST하는 기능이 있다.

스프링 부트에서는 JUnit을 활용하여 테스트 한다.

 

JUnit을 이용한 테스트 시 다음 네가지 종속성을 확인해야 한다.

JUNIT5, AssertJ, Mockito, H2Database

 

기본적으로 spring initializer로 프로젝트 생성 시 앞에 3개는 자동으로 종속되어 있다.

build.gradle의 dependency를 보면 확인이 가능하다.

 

따라서 h2 database만 따로 종속성을 추가해준다.

test시에 사용할 것이므로 다음 코드를 추가한다.

testImplementation 'com.h2database:h2:1.4.200'

 

test 사용 시 필요한 기능을 갖춘 종속성은 모두 추가해주었다.

 

그렇다면 test는 어디서 진행되는 것일까?

 

 

디렉토리를 보면 main과 test가 나란히 있는 것을 알 수 있다.

우리는 지금까지 main 이하에 디렉토리와 클래스를 생성해가며 실행했었지만, 나란히 존재하는 test 디렉토리에서는 unit test를 진행할 수 있다. 여기서 test를 실행한다.

 

실제 프로젝트가 돌아가는 동작은 main에서 일어난다. main에도 java와 resources 하위 디렉토리가 존재한다.

따라서 test를 위한 test 디렉토리 하에도 java와 병렬하게 resources 디렉토리를 생성해주자.

resources 디렉토리를 생성하였으면 그 안에 application.yml 파일을 추가해준다.

 

테스트 시 사용하는 데이터베이스는 h2이기 때문에, 스프링부트에 내장된 h2 데이터베이스를 연동하기 위해서 아래 코드를 yml파일에 추가해준다.

spring:
  datasource:
    url: jdbc:h2://mem:db;DB_CLOSE_DELAY=-1
    username: sa
    password: sa
    driver-class-name: org.h2.Driver

  # The SQL dialect makes Hibernate generate better SQL for the chosen database
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.H2Dialect
    show-sql: true

  #JPA settings
  jpa.hibernate.ddl-auto: create-drop

 

create-drop으로 하는 이유는 스프링부트를 종료했을 때 모두 drop하고 새로운 애플리케이션이 돌아갔을 때 새로 받기 위함이다.

 

💡 Repository test

 

이제 테스트를 할 기초 준비를 마쳤으니, repository의 동작부터 test해보자.

 

main의 형태와 동일하게 test에서도 repository 디렉토리를 만들어주고, 그 아래 CloudVendorRepositoryTest 클래스를 추가하여 CloudVendorRepository를 test한다.

 

 

이제 클래스를 test클래스로 정의해주자.

 

repository test 클래스를 test하기 위해서 @DataJpaTest 어노테이션을 추가해 식별해준다.

이때, 테스트 작업을 할 때, 모든 초기화 작업을 수행하는 것이 모범 사례이다.

무슨 의미이냐 하면, 테스트를 할 때도 비교할 만한 대상이 있어야 하기 때문에, 테스트 용 데이터를 넣어준다는 것이다.

 

main에서 만들어준 CloudVendorRepository가 정상 동작하는 지 확인해야 하기 때문에, repository를 하나와, 데이터를 저장해주기 위해 CloudVendor 객체를 하나 정의해 준다.

 

  • 설정 메서드

 

설정 메서드를 통해 각 test케이스가 실행되기 전에, 미리 데이터베이스에 테스트 용 객체를 생성해준다.

위와 같은 값을 가지는 CloudVendor 객체를 생성해주고, 데이터 베이스에 저장해준다.

 

  • 해제 메서드

 

해제 메서드를 통해 각 test케이스가 실행되고 난 후에는, 다시 객체를 반환하여 메모리를 반환해준다.

 

 

  • 성공 테스트

먼저, 성공적으로 조회가 된 경우에 대한 테스트를 정의해보자.

  • 테스트 케이스를 사용할 때마다 @Test 어노테이션을 사용하여, 아래 메서드가 일반 메서드가 아니라, 테스트 케이스임을 식별할 수 있다. 따라서 이 표시가 있는 메서드 옆에는 개별적으로 실행할 수 있도록 재생 표시가 있는 것을 확인할 수 있다.
  • 이 test의 메서드 이름은 testFindByVendorName_Found이다.
  • cloudVendorRepository의 findByVendorName을 통해 "Amazon"을 이름으로 가진 cloudVendor를 찾도록 하여 조회 결과를 list에 저장한다.
  • assertThat을 통해 실제로 예상되는 값을 반환하는지 확인할 수 있다.
  • asserThat을 통해 list에 저장된 cloudVendor의 id가, 우리가 설정해준 Amazon의 id와 동일한지 확인한다.
  • asserThat을 통해 list에 저장된 cloudVendor의 Address가, 우리가 설정해준 Amazon의 Address와 동일한지 확인한다.
  • 모두 동일하다면 이 테스트는 성공적인 결과를 반환할 것이다.

 

첫번째 테스트에 대한 실행 결과 로그를 살펴보자.

 

select와 insert 쿼리가 실행되며 데이터베이스에 입력하고, 주어진 입력에 대해 조회한 것을 확인할 수 있다.

 

또한 EmbeddedDatabaseFactory 사용을 시작하는 것과 함께, 스프링부트에 내장된 jdbc h2 데이터베이스를 사용한다는 것을 확인할 수 있다.

즉, 테스트 시에는 내 pc에 있는 mysql db가 아닌, 내장된 h2 데이터베이스를 사용하여 테스트 한다.

 

  • 실패 테스트

다음으로, 조회에 실패한 경우에 대한 테스트를 정의해보자.

 

 

  • 데이터베이스에 존재하지 않는 cloudvendor 이름을 찾으려고 하는 실패 테스트 케이스에 대한 테스트이다.
  • 현재는 내장 db에 Amazon 객체만 들어가 있는 상태임을 명심하자.
  • 이 메서드는 testFindByVendorName_NotFound이다.
  • 마찬가지로, repository 조회를 GCP로 조회하고 그 결과를 list에 저장하도록 한다.
  • 당연히 내장 db에는 Amazon에 대한 데이터밖에 없기 때문에 GCP로 조회한 결과는 비어있어야 한다.
  • 이를 확인하기 위해 asserThat 메서드를 통해서 lsit가 비어 있음이 true임을 확인하도록 한다.
  • 이 assertion이 통과하면 리스트는 비어있다는 것이고, 이 테스트는 성공적으로 통과하게 된다.

 

💡 respository test 클래스에 대한 실행 결과

 레포지토리 테스트 클래스를 실행한 결과, 내부에 있는 unit 테스트 두개(성공사례, 실패사례) 모두 성공적으로 잘 테스트 되었음을 ✔표시를 통해 확인할 수 있다.

 

만약 test case가 잘못 적용된다면 어떻게 될까? 다음과 같이 실패 케이스에서 isTrue 부분을 isFalse로 바꾸어 보자.

 

 

테스트 결과 NotFound 테스트에서는 실패 표시가 나타나고, 왜 실패했는지 옆에 로그를 통해 설명해주고 있다.

테스트 로직 내에서는 FALSE를 기대했으나, 실제 값은 TRUE를 반환하므로 테스트 결과가 실패로 뜨고 있다.

따라서 이런 로그를 통해 테스트 내에서 어떤 값을 예상하고 출력하는지 확인함으로써 수정할 수 있다.

 

이렇게 Repository test를 수행할 수 있다!

 

 

↓↓↓ 다음 내용

2024.01.05 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (7) - JUnit TEST service 계층 test 예제

 

[REST API] Spring Boot로 REST API CRUD 간단 구현 (7) - JUnit TEST service 계층 test 예제

↓↓↓ 이전 내용 2023.12.30 - [Web Application/Backend] - [REST API] Spring Boot로 REST API CRUD 간단 구현 (6) - JUnit TEST 기본 설정 및 repository 계층 test 예제 [REST API] Spring Boot로 REST API CRUD 간단 구현 (6) - JUnit TEST

im-gonna.tistory.com

 

반응형

+ Recent posts