관리 메뉴

제뉴어리의 모든것

JUnit 에 대하여 (1) - 통합 테스트 본문

Spring Boot/Test

JUnit 에 대하여 (1) - 통합 테스트

제뉴어리맨 2022. 12. 22. 02:15

전체 목록

  • 통합 테스트란
  • 의존성
  • 메소드 레벨 기본 어노테이션 종류
  • 통합 테스트에 필요한 어노테이션 종류
  • 추가적으로 사용되는 어노테이션
  • 테스트시 팁
  • 참조

해당 내용은 Junit5 기준입니다

 


통합 테스트란

  •  실제 운영 환경에서 사용될 클래스들을 통합하여 테스트 한다.
  • 단위 테스트와 같이 기능 검증을 위한 것이 아니라 spring framework에서 전체적으로 플로우가 제대로 동작하는지 검증하기 위해 사용 한다.

장점

  • 애플리케이션의 설정, 모든 Bean을 모두 로드하기 때문에 운영환경과 가장 유사한 테스트가 가능하다.
  • 전체적인 Flow를 쉽게 테스트 가능하다.

 

장점

  • 애플리케이션의 설정, 모든 Bean을 모두 로드하기 때문에 시간이 오래걸리고 무겁다.
  • 테스트 단위가 크기 때문에 디버깅이 어려운 편이다.

 

 

 

 

 

 


의존성

dependencies {
	..
   	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	..
}

 

해당 의존성에는 아래 라이브러리들이 포함되어 있다

 

  •  JUnit 5 (including the vintage engine for backward compatibility with JUnit 4): The de-facto standard for unit testing Java applications.
  • Spring Test & Spring Boot Test: Utilities and integration test support for Spring Boot applications.
  • AssertJ: A fluent assertion library.
  • Hamcrest: A library of matcher objects (also known as constraints or predicates).
  • Mockito: A Java mocking framework.
  • JSONassert: An assertion library for JSON.
  • JsonPath: XPath for JSON.

 

 

 


메소드 레벨 기본 어노테이션 종류

어노테이션 설명
@Test 테스트 메소드를 나타내는 어노테이션. 필수로 작성되어야 한다.
@BeforeEach 각 테스트 메소드 시작 전에 실행되어야 하는 메소드에 써준다.
@AfterEach 각 테스트 메소드 종료 후에 실행되어야 하는 메소드에 써준다.
@BeforeAll 테스트 시작 전에 실행되어야 하는 메소드에 써준다. (static 메소드여야만 함)
@AfterAll 테스트 종료 후에 실행되어야 하는 메소드에 써준다. (static 메소드여야만 함)
@Disabled 실행되지 않아야 하는 테스트 메소드에서 써준다.

 


통합 테스트에 필요한 어노테이션 종류

 

어노테이션 설명
@SpringBootTest - 통합 테스트 용도로 사용됨

- @SpriBootApplication을 찾아가 하위의 모든 Bean을 스캔하여 로드함

- 그 후 Test용 Application Context를 만들어 Bean을 추가하고, MockBean을 찾아 교체
@Transactional
- 클래스 내부의 각각의 테스트 메소드가 실행될때마다, 데이터베이스를 롤백함

- 만약, 테스트 후 데이터를 직접 눈으로 보기 위해 데이터를 롤백하지 않고 싶다면, 메소드위에 @Rollback(false)을 부여하면 됨


@AutoConfigureMockMvc
- MockMvc 객체를 이용하여 API 테스트를 해야할 때 사용
- @SpringBootTest를 사용하지 않고 @WebMvcTest어노테이션을 사용하는 경우, MockMvc는 자동으로 설정되며, 동시에 @AutoConfigureMockMvc를 사용하게 되면, 충돌이 발생된다

 

@SpringBootTest 어노테이션 옵션

 

1. properties

key-value 형태의 속성값을 정의한다.

 

사용예시

@SpringBootTest( properties = { "testId=born", "testName=탄생" } )
//@SpringBootTest( value = { "testId=born", "testName=탄생" } )
//@SpringBootTest( { "testId=born", "testName=탄생" } )
class TestApplication {
    @Value("${testId}")
    private String testId; 
    
    @Value("${testName}")
    private String testName;
}

 

2. args

application의 agruments를 삽입한다

 

사용예시

@SpringBootTest( args = {"--app.test=one", "--app.test2=two"} )
class TestApplication {
    @Value("${app.test}")
    private String appTest;

    @Autowired
    private ApplicationArguments args;
    
    @BeforeEach
    public void setUp() {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }
}

 

 

3.classes

기본적으로 @SpringBootTest 는 모든 빈을 등록한다.

하지만 classes 속성을 정의한다면 해당 클래스의 빈만 정의된다. config 설정이 있다면 해당 classes로 등록해줘야 한다.

 

사용예시

@SpringBootTest(classes = {ArticleServiceImpl.class, CommonConfig.class})
class TestApplication {
    @Autowired
    private ArticleServiceImpl articleServiceImpl;

    @Autowired
    private RestTemplate restTemplate;
}

 

4. WebEnvironment

=> WebEnvironment.MOCK : 아무런 설정이 없을 시 적용되는 디폴트 설정이다.
   -- mock 서블릿 환경으로 내장톰캣이 구동되지 않는다.(브라우저에서 접속되지 않는다.)
   -- 실제 객체를 만들기엔 비용과 시간이 많이 들거나 의존성이 길게 걸쳐져 있어 제대로 구현하기 어려울 경우, 가짜 객체를 만들어 사용한다.
   -- WebApplicationContext를 로드하며 내장된 서블릿 컨테이너가 아닌 Mock 서블릿을 제공한다.
   -- 별도로 지정하지 않으면 기본값은 Mock 서블릿을 로드하여 구동하게 된다.
   -- @AutoConfigureMockMvc 어노테이션을 함께 사용하면 별다른 설정 없이 간편하게 MockMvc를 사용한 테스트를 진행할 수 있다.

=> WebEnvironment.RANDOM_PORT :

  -- EmbeddedWebApplicationContext를 로드하며 스프링부트를 직접 구동시킨 것처럼 내장톰캣이 구동되나 랜덤포트로 구동된다.

 

=> WebEnvironment.DEFINED_PORT :

   -- RAMDOM_PORT와 동일하게 실제 서블릿 환경을 구성하지만, 포트는 애플리케이션 프로퍼티에서 지정한 포트를 listen 한다.


=> WebEnvironment.NONE : WebApplicationType.NONE으로 구동된다.

   -- - 기본적인 ApplicationContext를 로드한다.

 

 

RANDOM_PORT 또는 DEFINED_PORT 사용시 주의사항

@SpringBootTest를 RANDOM_PORT나 DEFINED_PORT로 사용하면 별도의 쓰레드에서 스프링 컨테이너가 실행된다. 테스트가 끝나고 이를 롤백시키려면 하나의 트랜잭션으로 묶여야 하는데, 스프링 컨테이너가 실제로 구동되어 테스트와 다른 쓰레드에서 실행되니 하나의 트랜잭션으로 묶일 수 없는 것이다. 그래서 @SpringBootTest를 RANDOM_PORT나 DEFINED_PORT로 사용하면 @Transactional을 사용해도 롤백되지 않는다

 

해결방법

 


추가적으로 사용되는 어노테이션

 

어노테이션 설명
@ActiveProfiles - 특정 프로파일을 사용하고 싶을 때 사용

 


테스트시 팁

given, when, then

- 테스트 코드 작성시, 많은 곳에서 추천하는 코딩 스타일인데요, 어떤값이 주어지고(given), 무엇을 했을때(when), 어떤 값을 원한다(then)을 나누어 직관적으로 볼 수 있기 때문에, 테스트 코드의 가독성이 향상됩니다.

- 테스트 코드의 가독성이 중요한 또 다른 이유는, 테스트 코드가 문서로써의 역할을 하기도 하기 때문입니다. 테스트코드를 봄으로써, 해당 메소드를 작성한 개발자가 어떤의도로 만들었으며, 어떻게 동작하길 원하는지를 알 수있습니다.

 

 

모든 response에 대한 테스트를 진행한다.

- api가(테스트대상) 조금이라도 수정될 경우, 테스트코드가 실패하게 됨으로써, 항상 올바른 테스트 코드를 유지할 수 있도록 돕습니다.

- api가(테스트대상) 변경되면, 테스트 코드역시 변경되어야 하는 것은 당연합니다.

- 테스트 코드는 커버리지가 높을 수록 좋습니다. 테스트 코드는, 정상적으로 작동하는 부분만을 테스트 하면 안됩니다, 테스트 코드는 실수나 오류를 발견하고 이를 줄이고 수정하기 위해 작성하는 것입니다.

 

 

회원 조회등을 위한 생성 메서드를 만든다.

통합테스트를 진행하면, 데이터베이스의 특정 데이터에 의존하는 테스트가 존재하기 마련입니다. 이때, 조회 메소드의 테스트에 집중하기위해, 특정 데이터를 생성하는 테스트 메소드를 별도로 만들어 호출하는 것으로, 테스트를 용이하게 진행합니다.

 

 

 

참조

  • 메소드 레벨 기본 어노테이션 종류
    https://astrid-dm.tistory.com/536
    https://effortguy.tistory.com/113

  • @SpringBootTest 옵션
    https://myborn.tistory.com/15
    https://goddaehee.tistory.com/211

  • RANDOM_PORT 또는 DEFINED_PORT 사용시 주의사항
    https://mangkyu.tistory.com/264

  • 테스트시 팁
    https://galid1.tistory.com/735