[Test] Spring 공식문서 Testing 중 일부

2022. 12. 19. 18:18Test

최근 계속해서 테스트의 중요성과 TDD를 학습하면서 Test 관련 내용을 공부하고 있습니다. 지금까지는 다양한 테스트 도구의 사용법을 익혔는데, '이를 좀 잘 정리할 방법이 없을까?'라고 생각하다가 여윽시 근본은 공식문서라는 생각이 들어서 번역겸 정리를 해보고자 합니다.

또한 인프콘 세션을 정리하면서 결국 테스트를 통해 spring 본연의 기능을 더 깊이 있게 이해할 수 있다고 판단했습니다.


공식문서 목차

  1. Introduction to Spring Testing
  2. Unit Testing
  3. Integration Testing
  4. JDBC Testing Support
  5. Spring TestContext Framework
  6. WebTestClient
  7. MockMvc
  8. Testing Client Applications
  9. Appendix

이 중에서 1~4장까지의 내용만 해당 글에서 정리를 하였습니다. 5장부터는 사용해보지 않은 부분들도 있어서, 사용해본 스택과 그러지 않은 스택을 적절히 구분하여 작성 후 링크를 걸어둘 예정입니다.


1. Introduction

스프링 공식문서에서는 스프링 프레임워크에서 TDD를 지원하기 위한 방법들을 제시하는데, 올바른 방식으로 IoC를 사용하면 유닛테스트와 통합 테스트를 더 쉽게 만들 수 있다고 소개하고 있습니다. 단위 테스트에 IoC원칙이 추가한 가치와 통합 테스트를 위해 스프링 프레임워크가 지원하는 내용들에 초점을 맞추고 있다고 합니다. ( 기업의 비즈니스 로직에 대한 테스트 처리는 참조 메뉴얼의 범위가 아니라고 적혀있네요. ^^ )

 

위 내용인즉슨 테스트 코드 작성도 객체지향적으로 '잘' 해야된다는 의미로 받아들지며, IoC를 사용함으로서 객체지향 프로그래밍의 장점을 극대화했다고 이해했습니다.

 


2. Unit Testing

가장 기본적인 이야기이지만, 테스트를 단위화 하고 격리시켜서 테스트를 가능하도록 하는 기술적인 방법을 제시한다고 합니다. 스프링에서 제시하는 권장 사항을 따르는 경우 결과적으로 코드베이스의 깔끔한 계층화 및 구성 요소화가 더 쉬운 단위테스트를 작성할 수 있다고합니다. 이를 위해서 Mock Object와 테스트 관련 class들을 지원하고 있습니다.

 

단위 테스트는 일반적으로 설정할 런타임 인프라가 없기 때문에 실행 속도가 빠르다는  특징이 있습니다. TDD를 사용하는 맥락에서 이러한 이유로 테스트를 도입하면 생산성을 높일 수 있다고합니다. 반복되는 TDD에 대한 개념은 링크에 간략히 정리해 두었습니다.

 

Mock에 대해서만 간단히 의미를 정의하고 넘어가겠습니다. 아래는 위키에 정의 된 내용입니다.

모의 객체(Mock Object)란 주로 객체 지향 프로그래밍으로 개발한 프로그램을 테스트 할 경우 테스트를 수행할 모듈과 연결되는 외부의 다른 서비스나 모듈들을 실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는데 사용하는 객체이다. 사용자 인터페이스(UI)나 데이터베이스 테스트 등과 같이 자동화된 테스트를 수행하기 어려운 때 널리 사용된다.

Mock Object

스프링은 Mock Object를 지원하기 위해서 'Environment', 'JNDI', 'Servlet API', 'Spring Web Reactive' 패키지를 포함하고 있습니다.

 

  1. Environmentorg.springframework.mock.env 패키지에는 Environment 및 PropertySource 추상화의 모의 구현이 포함되어 있습니다(Bean 정의 프로필 및 PropertySource 추상화 참조). MockEnvironment 및 MockPropertySource는 환경별 속성에 의존하는 코드에 대한 컨테이너 외부 테스트를 개발하는 데 유용합니다.
  2. JNDIorg.springframework.mock.jndi 패키지에는 JNDI SPI의 부분 구현이 포함되어 있어 테스트 스위트 또는 독립형 애플리케이션을 위한 간단한 JNDI 환경을 설정하는 데 사용할 수 있습니다. 예를 들어 JDBC DataSource 인스턴스가 Jakarta EE 컨테이너에서와 마찬가지로 테스트 코드에서 동일한 JNDI 이름에 바인딩되면 수정 없이 테스트 시나리오에서 애플리케이션 코드와 구성을 모두 재사용할 수 있습니다.
  3. Servlet APIorg.springframework.mock.web 패키지에는 웹 컨텍스트, 컨트롤러 및 필터를 테스트하는 데 유용한 포괄적인 Servlet API 목 객체 세트가 포함되어 있습니다. 이러한 목 객체는 Spring의 웹 MVC 프레임워크와 함께 사용하는 것을 목표로 하며 일반적으로 동적 목 객체(예: EasyMock) 또는 대체 서블릿 API 목 객체(예: MockObjects)보다 사용하기 더 편리합니다.Spring MVC 테스트 프레임워크는 Spring MVC용 통합 테스트 프레임워크를 제공하기 위해 모의 Servlet API 객체를 기반으로 합니다. MockMvc를 참조하십시오.
  4. Spring Web Reactiveorg.springframework.mock.http.server.reactive 패키지에는 WebFlux 애플리케이션에서 사용하기 위한 ServerHttpRequest 및 ServerHttpResponse의 모의 구현이 포함되어 있습니다. org.springframework.mock.web.server 패키지에는 이러한 모의 요청 및 응답 객체에 의존하는 모의 ServerWebExchange가 포함되어 있습니다.

    MockServerHttpRequest와 MockServerHttpResponse는 모두 서버별 구현과 동일한 추상 기본 클래스에서 확장되며 동작을 공유합니다. 예를 들어 모의 요청은 일단 생성되면 변경할 수 없지만 ServerHttpRequest의 mutate() 메서드를 사용하여 수정된 인스턴스를 생성할 수 있습니다.

    모의 응답이 쓰기 계약을 제대로 구현하고 쓰기 완료 핸들(즉, Mono<Void>)을 반환하기 위해 기본적으로 캐시().then()과 함께 Flux를 사용하여 데이터를 버퍼링하고 테스트의 어설션에 사용할 수 있습니다. 애플리케이션은 사용자 지정 쓰기 기능을 설정할 수 있습니다(예: 무한 스트림 테스트).

    WebTestClient는 HTTP 서버 없이 WebFlux 애플리케이션 테스트를 지원하기 위해 모의 요청 및 응답을 기반으로 합니다. 클라이언트는 실행 중인 서버에서 종단 간 테스트에도 사용할 수 있습니다.

 

Unit Testing Support Classes

스프링 프레임워크는 'General Testing Utilities'와 'Spring MVC Testing Utilities'를 포함하고 있습니다.

 

General Testing Utilities에는 단위 및 통합테스트에 사용할 여러 범용 유틸리티가 포함되어 있습니다.

  • AopTestUtils는 AOP 관련 유틸리티 메서드 모음입니다. AOP가 기본적으로 프록시 패턴으로 기본 대상 객체들에 대한 정보를 참조할 수 있는 특성을 이용하는 방식으로 이해가 됩니다. EasyMock 또는 Mockito와 같은 라이브러리를 사용하여 Bean을 동적으로 모의 구성하고 이를 사용하여 기대값을 설정하고 검증을 진행할 수 있다고 합니다. ( AopUtils, AopProxyUtils )
  • ReflectionTestUtils는 리플렉션 기반 유틸리티 메서드 모음입니다. 애플리케이션 코드를 테스트할 때 상수를 변경하거나, 비공개 필드를 설정하거나, 비공개 setter 메서드를 호출하거나, 비공개 구성 또는 수명 주기 콜백 메서드를 호출해야 하는 테스트 시나리오에서 이러한 메서드를 사용할 수 있습니다.

Spring MVC Testing Utilities은 org.springframework.test.web 패키지를 포함하는데 이 패키지에는 JUnit, TestNG 또는 Spring MVC ModelAndView 객체를 다루는 단위 테스트를 위한 다른 테스트 프레임워크와 함께 사용할 수 있는 ModelAndViewAssert가 포함되어 있습니다.

 

문서에서 Tip으로 주어지는 내용인데, Spring MVC 및 REST 컨트롤러 클래스의 통합 테스트를 위해서는 Spring MVC 테스트 프레임워크 사용을 권장하고 있습니다. 다른 강좌를 보면서 거의 MockMvc를 사용했었습니다. ( 개발 경험의 부족 ㅠㅠ )

단위 테스트 Spring MVC 컨트롤러 Spring MVC Controller 클래스를 POJO로 단위 테스트하려면 Spring의 Servlet API 목에서 MockHttpServletRequest, MockHttpSession 등과 결합된 ModelAndViewAssert를 사용하십시오. Spring MVC용 WebApplicationContext 구성과 함께 Spring MVC 및 REST 컨트롤러 클래스의 철저한 통합 테스트를 위해 대신 Spring MVC 테스트 프레임워크를 사용하십시오.

 


3. Integration Testing

애플리케이션 서버에 배포하거나 다른 엔터프라이즈 인프라에 연결하지 않고도 일부 통합 테스트를 수행할 수 있어야 하는데, 그게 가능하려면 기본적으로 아래 내용을 테스트가 가능해야 합니다.

  • Spring IoC 컨테이너 컨텍스트의 올바른 연결
  • JDBC 또는 ORM 도구를 사용한 데이터 액세스. 여기에는 SQL 문의 정확성, Hibernate 쿼리, JPA 엔터티 매핑 등과 같은 것들이 포함됨

위 내용들은 사실 실제 테스트를 진행할 때 스프링이 무엇을 제공해주는지 잘 몰라서 테스트가 어려운 부분이라고 생각했습니다.

 

스프링 프레임워크에서는 스프링 컨테이너와의 통합테스트에 유용한 클래스가 포함되어 있습니다.단위 테스트보다는 실행 속도가 느리지만 애플리케이션 서버에 의존해서 매번 서비스를 배포하여 띄우고 테스트해야하는 번거로움을 줄일 수 있습니다. 이를 지원하기 위해서는 어노테이션 기반으로 SpringTestContext Framework의 형태로 제공됩니다. TestContext 프레임워크는 사용 중인 실제 테스트 프레임워크와 무관하기 때문에 JUnit 등을 비롯한 다양한 환경에서 테스트가 가능합니다.

 

Spring의 통합 테스트를 지원하는 도구들의 목표는 아래와 같습니다.

  • 테스트 간 Spring IoC 컨테이너 캐싱을 관리( Context Management and Caching )
    • SpringApplicationContext 인스턴스 및 WebApplicationContext 인스턴스의 일관된 로드와 캐싱을 제공합니다.
    • 테스트 코드를 동작할 때, 첫 실행은 뭔가 시간이 조금 걸리는데 두번째 부터는 빠르게 실행되는 경험을 해보셨을 겁니다. 그게 바로 테스트 간 Spring IoC 컨테이너에 대한 메타데이터들이 캐싱되고 관리되기 때문입니다.
    • Context Management, Context Caching에 대한 내용은 5장에서 상세하게 다룹니다.
  • 테스트 fixture instance의 의존성 주입을 제공합니다.
    • TestContext 프레임워크가 애플리케이션 컨텍스트를 로드할 때 종속성 주입을 사용하여 테스트 클래스의 인스턴스를 선택적으로 구성 할 수 있습니다.
    • 즉 테스트에 필요한 대상들을 IoC 컨테이너를 통해 선택적으로 주입할 수 있다는 것인데, 보통 @Autowired 어노테이션을 사용하는 부분이라고 이해를 했습니다.
  • 통합 테스트에 적합한 트랜잭션 관리를 제공합니다.
    • 실제 데이터베이스에 액세스하는 테스트에서 영속성 컨텍스트에 대한 관리가 제대로 되지 않으면 나머지 테스트를 제대로 수행할 수 없습니다. 가령 롤백이 되지 않아서 첫번째 테스트 후 두번째 테스트에 영향을 준다던지 말입니다.
    • TestContext 프레임 워크는 각 테스트에 대한 트랜잭션을 생성하고 롤백을 하도록합니다.
    • 만약 Commit이 필요한 경우라면 @Commit을 사용하여 커밋을 할 수도 있습니다.
  • 개발자가 통합 테스트를 작성하는 데 도움이 되는 Spring 특정 기본 클래스를 제공합니다.

 


4. JDBC Testing Support

org.springframework.test.jdbc 패키지에는 표준 데이터베이스 테스트 시나리오를 단순화하기 위한 JDBC 관련 유틸리티 기능 모음인 JdbcTestUtils가 포함되어 있습니다. 특히 JdbcTestUtils는 다음과 같은 정적 유틸리티 메서드를 제공합니다.

  • countRowsInTable(..): 주어진 테이블의 행 수를 계산합니다.
  • countRowsInTableWhere(..): 제공된 WHERE 절을 사용하여 주어진 테이블의 행 수를 계산합니다.
  • deleteFromTables(..): 지정된 테이블에서 모든 행을 삭제합니다.
  • deleteFromTableWhere(..): 제공된 WHERE 절을 사용하여 주어진 테이블에서 행을 삭제합니다.
  • dropTables(..): 지정된 테이블을 삭제합니다.

spring-jdbc 모듈은 데이터베이스와 상호 작용하는 통합 테스트에서 사용할 수 있는 임베디드 데이터베이스의 구성 및 시작에 대한 지원을 제공합니다.

 


마무리

생각보다 이런게 있었구나... 하게되는 부분들이 많아서 씁쓸하기도하고 부끄럽기도 하였습니다. 위 내용들은 개략적인 내용으로 조금 더 상세한 부분들은 5장부터가 될 것 같습니다.

 

일단 5장부터는 위에서도 언급하였듯, 사용해본 스택들 위주로 정리를 다른 글에서 이어나가고 링크를 걸어두고자 합니다.

이상 - 끝 -