티스토리 뷰

https://www.baeldung.com/spring-tests

주요 부분이라고 생각한 것만 한글화.

 

2. Integration Tests

통합 테스트는 자동화된 테스트 suites의 기본적인 부분입니다. 하지만 건강한 테스트 피라미드를 따른다면 단위 테스트만큼 많지는 않아야 합니다. Spring과 같은 프레임워크에 의존하면 시스템의 특정 동작에 대한 위험을 제거하기 위해 상당한 양의 통합 테스트가 필요합니다.

Spring 모듈(데이터, 보안, 소셜 등)을 사용하여 코드를 더 단순화할수록 통합 테스트의 필요성은 더 커집니다. 특히 인프라의 일부분을 @Configuration 클래스로 옮길 때 더욱 그렇습니다.

"프레임워크를 테스트"해서는 안 되지만, 프레임워크가 우리의 요구 사항을 충족하도록 구성되어 있는지 확실히 확인해야 합니다.

통합 테스트는 신뢰를 구축하는 데 도움이 되지만 대가가 따릅니다:

  • 실행 속도가 느려지므로 빌드 속도가 느려집니다.
  • 또한 통합 테스트는 더 넓은 테스트 범위를 의미하므로 대부분의 경우 이상적이지 않습니다.

 

3. Testing Web Apps

Spring에는 웹 애플리케이션을 테스트하기 위한 몇 가지 옵션이 있으며, 대부분의 Spring 개발자는 이러한 옵션에 익숙합니다:

  • MockMvc: 비반응형 웹 앱에 유용한 서블릿 API를 모의 처리 합니다.
  • TestRestTemplate: 특정 앱을 가리키는 데 사용할 수 있으며, 모의 서블릿이 바람직하지 않은 비반응형 웹 앱에 유용합니다.
  • 웹테스트클라이언트: 모의 요청/응답을 사용하거나 실제 서버를 호출하는 반응형 웹 앱용 테스트 도구입니다.

이미 이러한 주제를 다루는 기사가 있으므로 이에 대해서는 따로 설명하지 않겠습니다.
더 자세히 알아보고 싶으시다면 언제든지 찾아보시기 바랍니다.

 

4. Optimizing Execution Time

통합 테스트는 훌륭합니다. 통합 테스트는 우리에게 상당한 자신감을 줍니다. 또한 적절하게 구현하면 앱의 의도를 매우 명확하게 설명할 수 있으며, 모의 테스트와 설정 노이즈가 줄어듭니다.

하지만 앱이 성숙해지고 개발이 쌓이면 빌드 시간은 필연적으로 늘어납니다. 빌드 시간이 길어지면 매번 모든 테스트를 계속 실행하는 것이 비현실적으로 될 수 있습니다.

그 후에는 피드백 루프에 영향을 미치고 모범 개발 사례를 따르는 데 방해가 됩니다.

또한 통합 테스트는 본질적으로 비용이 많이 듭니다. 일종의 지속성을 시작하거나, 요청을 전송하거나(로컬호스트를 벗어나지 않더라도), 일부 IO를 수행하는 데는 시간이 걸립니다.

테스트 실행을 포함하여 빌드 시간을 주시하는 것이 가장 중요합니다. 그리고 이 시간을 줄이기 위해 Spring에서 적용할 수 있는 몇 가지 트릭이 있습니다.

다음 섹션에서는 빌드 시간을 최적화하는 데 도움이 되는 몇 가지 사항과 속도에 영향을 줄 수 있는 몇 가지 함정에 대해 알아보겠습니다:

  • 현명한 프로필 사용 - 프로필이 성능에 미치는 영향
  • MockBean 재검토 - 모킹이 성능에 미치는 영향
  • MockBean 리팩토링 - 성능 향상을 위한 대안
  • DirtiesContext에 대해 신중하게 생각하기 - 유용하지만 위험한 주석과 이를 사용하지 않는 방법
  • 테스트 슬라이스 사용 - 도움이 되거나 방해가 될 수 있는 멋진 도구
  • 클래스 상속 사용 - 안전한 방식으로 테스트를 구성하는 방법
  • 상태 관리 - 엉성한 테스트를 방지하는 모범 사례
  • 단위 테스트로 리팩토링하기 - 견고하고 빠른 빌드를 위한 가장 좋은 방법

 

4.1. Using Profiles Wisely

프로필은 매우 깔끔한 도구입니다. 즉, 앱의 특정 영역을 활성화 또는 비활성화할 수 있는 간단한 태그입니다. 심지어 기능 플래그를 구현할 수도 있습니다!

프로필이 더 풍부해지면 통합 테스트에서 가끔씩 프로필을 바꾸고 싶을 때가 있습니다. 액티브 프로필과 같은 편리한 도구가 있습니다. 하지만 새 프로필로 테스트를 실행할 때마다 새 ApplicationContext가 생성됩니다.

아무것도 없는 바닐라 스프링 부팅 앱을 사용하면 애플리케이션 컨텍스트를 빠르게 생성할 수 있습니다. ORM과 몇 개의 모듈을 추가하면 7초 이상으로 빠르게 늘어납니다.

여러 프로필을 추가하고 몇 가지 테스트를 통해 분산하면 60초 이상의 빌드를 빠르게 얻을 수 있습니다(빌드의 일부로 테스트를 실행한다는 가정 하에, 그리고 실제로 실행해야 합니다).

일단 충분히 복잡한 애플리케이션에 직면하게 되면 이 문제를 해결하는 것은 매우 어렵습니다. 하지만 미리 신중하게 계획을 세운다면 합리적인 빌드 시간을 유지하는 것은 그리 어려운 일이 아닙니다.

통합 테스트의 프로필과 관련하여 염두에 두어야 할 몇 가지 요령이 있습니다:

  • 종합 프로필(예: 테스트)을 생성하고, 그 안에 필요한 모든 프로필을 포함하세요 - 어디에서나 테스트 프로필을 고수하세요.
  • 테스트 가능성을 염두에 두고 프로필을 디자인하세요. 프로필을 전환해야 하는 경우 더 나은 방법이 있을 수 있습니다.
  • 테스트 프로필을 중앙 집중식 장소에 명시하세요 - 이에 대해서는 나중에 설명하겠습니다.
  • 모든 프로필 조합을 테스트하지 마세요. 또는 특정 프로필 세트로 앱을 테스트하는 환경별 e2e 테스트 스위트를 만들 수 있습니다.

 

4.2. The Problems with @MockBean

모의빈은 매우 강력한 도구입니다.

Spring의 마법이 필요하지만 특정 컴포넌트를 모방하고 싶을 때 @MockBean은 매우 유용합니다. 하지만 대가가 따릅니다.

클래스에서 @MockBean이 나타날 때마다 ApplicationContext 캐시가 더티로 표시되므로 테스트 클래스가 완료된 후 런너가 캐시를 정리합니다. 이는 다시 빌드에 몇 초를 더 추가합니다.

이것은 논란의 여지가 있지만, 이 특정 시나리오에 대해 모의 테스트 대신 실제 앱을 실행해 보는 것이 도움이 될 수 있습니다. 물론 여기에 만병통치약은 없습니다. 종속성 모킹을 허용하지 않으면 경계가 모호해집니다.

테스트하고 싶은 것이 REST 계층뿐인데 왜 계속 테스트해야 하냐고 생각할 수 있습니다. 이는 타당한 지적이며, 항상 타협점이 존재합니다.

하지만 몇 가지 원칙을 염두에 두면 테스트와 앱을 더 잘 설계하고 테스트 시간을 단축할 수 있는 이점으로 전환할 수 있습니다.

 

4.3. Refactoring @MockBean

이 섹션에서는 @MockBean을 사용하여 '느린' 테스트를 리팩터링하여 캐시된 ApplicationContext를 재사용하도록 해 보겠습니다.

사용자를 생성하는 POST를 테스트하고 싶다고 가정해 보겠습니다. 모의 테스트에서 @MockBean을 사용하면 서비스가 제대로 직렬화된 사용자로 호출되었는지 간단히 확인할 수 있습니다.

서비스를 제대로 테스트했다면 이 방법으로도 충분할 것입니다:

하지만 @MockBean은 피하고 싶습니다. 따라서 결국 엔티티를 유지하게 될 것입니다(서비스가 그렇게 한다고 가정할 때).

여기서 가장 순진한 접근 방식은 부작용을 테스트하는 것입니다: 게시 후 내 사용자는 내 DB에 있으며, 이 예제에서는 JDBC를 사용합니다.

그러나 이는 테스트 경계를 위반하는 것입니다:

이 특정 예제에서는 사용자를 전송하기 위해 앱을 HTTP 블랙박스로 취급하지만 나중에 구현 세부 정보, 즉 사용자가 일부 DB에 지속되었다고 주장하기 때문에 테스트 경계를 위반합니다.

HTTP를 통해 앱을 실행하면 결과도 HTTP를 통해 어설트할 수 있을까요?

마지막 접근 방식을 따르는 경우 몇 가지 장점이 있습니다:

테스트가 더 빨리 시작됩니다(실행하는 데 시간이 조금 더 걸릴 수 있지만, 그만한 가치가 있습니다).
또한, 테스트는 HTTP 경계와 관련이 없는 부작용(예: DB)을 인식하지 못합니다.
마지막으로, 이 테스트는 시스템의 의도를 명확하게 표현합니다: POST하면 GET 사용자가 가능합니다.
물론 여러 가지 이유로 항상 가능하지 않을 수도 있습니다:

'부작용' 엔드포인트가 없을 수도 있습니다: 여기서 한 가지 옵션은 '테스트 엔드포인트' 생성을 고려하는 것입니다.
앱 전체에 적용하기에는 복잡성이 너무 높은 경우: 슬라이스를 고려하는 옵션이 있습니다(이에 대해서는 나중에 설명하겠습니다).

 

4.4. Thinking Carefully About @DirtiesContext

때때로 테스트에서 ApplicationContext를 수정해야 할 수도 있습니다. 이 시나리오의 경우 @DirtiesContext가 정확히 그 기능을 제공합니다.

위에서 설명한 것과 같은 이유로 @DirtiesContext는 실행 시간 측면에서 매우 비싼 리소스이므로 주의해야 합니다.

애플리케이션 캐시 재설정 또는 메모리 내 DB 재설정을 위해 @DirtiesContext를 오용하는 경우도 있습니다. 통합 테스트에서 이러한 시나리오를 처리하는 더 좋은 방법이 있으며, 다음 섹션에서 몇 가지를 다룰 것입니다.

 

4.5. Using Test Slices

테스트 슬라이스는 1.4에 도입된 Spring Boot 기능입니다. 개념은 매우 간단합니다. Spring은 앱의 특정 슬라이스에 대해 축소된 애플리케이션 컨텍스트를 생성합니다.

또한 프레임워크는 최소한의 구성만 처리합니다.

Spring Boot에는 기본적으로 사용할 수 있는 적절한 수의 슬라이스가 있으며, 자체 슬라이스를 만들 수도 있습니다:

  • @JsonTest: JSON 관련 컴포넌트를 등록합니다.
  • @DataJpaTest: 사용 가능한 ORM을 포함한 JPA 빈을 등록합니다.
  • @JdbcTest: 원시 JDBC 테스트에 유용하며, 데이터 소스 및 메모리 내 DB를 ORM 없이 처리합니다.
  • 데이터몽고테스트: 인메모리 몽고 테스트 설정을 제공하려고 시도합니다.
  • WebMvcTest: 앱의 나머지 부분이 없는 모의 MVC 테스트 슬라이스
  • ... (소스를 확인하여 모두 찾을 수 있음)

이 기능을 현명하게 사용하면 특히 중소규모 앱의 경우 성능 측면에서 큰 불이익 없이 좁은 범위의 테스트를 구축하는 데 도움이 될 수 있습니다.

하지만 애플리케이션이 계속 커지면 슬라이스당 하나의 (작은) 애플리케이션 컨텍스트가 생성되므로 이 또한 쌓이게 됩니다.

 

4.6. Using Class Inheritance

모든 통합 테스트의 부모로 단일 AbstractSpringIntegrationTest 클래스를 사용하는 것은 빌드를 빠르게 유지하는 간단하고 강력하며 실용적인 방법입니다.

우리가 탄탄한 설정을 제공하면, 우리 팀은 모든 것이 '그냥 작동'한다는 것을 알고 간단히 확장할 수 있습니다. 이렇게 하면 상태 관리나 프레임워크 구성에 대한 걱정을 덜고 당면한 문제에 집중할 수 있습니다.

모든 테스트 요구 사항을 설정할 수 있습니다:

  • Spring 주자 - 나중에 다른 주자가 필요할 경우를 대비해 규칙을 설정하는 것이 좋습니다.
  • 프로필 - 이상적으로는 우리의 종합 테스트 프로필
  • 초기 구성 - 애플리케이션의 상태 설정


앞의 사항을 처리하는 간단한 베이스 클래스를 살펴봅시다:

 

4.7. State Management

단위 테스트의 '단위'가 어디에서 왔는지 기억하는 것이 중요합니다. 간단히 말해, 단일 테스트(또는 하위 집합)를 언제든지 실행하여 일관된 결과를 얻을 수 있다는 뜻입니다.

따라서 모든 테스트가 시작되기 전에 상태가 깨끗하고 알려져 있어야 합니다.

즉, 테스트가 단독으로 실행되든 다른 테스트와 함께 실행되든 관계없이 테스트 결과가 일관적이어야 합니다.

이 아이디어는 통합 테스트에도 동일하게 적용됩니다. 새 테스트를 시작하기 전에 앱이 알려진(그리고 반복 가능한) 상태인지 확인해야 합니다. 속도를 높이기 위해 재사용하는 구성 요소(앱 컨텍스트, DB, 대기열, 파일 등)가 많을수록 상태 오염이 발생할 가능성이 높아집니다.

클래스 상속을 사용했다고 가정하면 이제 상태를 관리할 수 있는 중앙 집중식 공간이 생겼습니다.

테스트를 실행하기 전에 앱이 알려진 상태에 있는지 확인하기 위해 추상 클래스를 개선해 보겠습니다.

이 예제에서는 다양한 데이터 소스의 여러 리포지토리와 와이어목 서버가 있다고 가정하겠습니다:

 

4.8. Refactoring into Unit Tests

이것이 아마도 가장 중요한 포인트 중 하나일 것입니다. 실제로 앱의 상위 정책을 실행하는 몇 가지 통합 테스트를 반복해서 수행하게 될 것입니다.

핵심 비즈니스 로직의 여러 사례를 테스트하는 통합 테스트를 발견할 때마다 접근 방식을 재고하고 단위 테스트로 세분화해야 할 때입니다.

이를 성공적으로 수행하기 위한 가능한 패턴은 다음과 같습니다:

  • 핵심 비즈니스 로직의 여러 시나리오를 테스트하는 통합 테스트를 식별합니다.
  • 제품군을 복제하고 복사본을 단위 테스트로 리팩터링 - 이 단계에서는 프로덕션 코드도 테스트할 수 있도록 세분화해야 할 수 있습니다.
  • 모든 테스트를 녹색으로 설정
  • 통합 제품군에서 충분히 주목할 만한 행복한 경로 샘플을 남겨둡니다 - 몇 가지를 리팩터링하거나 조인 및 재구성해야 할 수도 있습니다.
  • 나머지 통합 테스트 제거

 

공지사항