본문 바로가기
Test

JUnit

by oncerun 2022. 11. 8.
반응형

 

급한 불인 테스트 관련 지식을 우선적으로 습득해야겠다!

 

 

JUnit이 하나의 테스트 클래스를 가져와 수행하는 방식

 

1. 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메서드를 전부 찾는다.

 

2. 테스트 클래스의 오브젝트를 하나 만든다.

 

3. @BeforeEach가 붙은 메서드가 있으면 실행한다.

 

4. @Test가 붙은 메서드를 하나 호출하고 테스트 결과를 저장해둔다.

 

5. @AfterEach가 붙은 메서드가 있으면 실행한다.

 

6. 나머지 테스트를 반복하면서 2~5번을 실행

 

7. 테스트 결과를 종합하여 리턴한다.

 

기억 : 새로운 테스트 메서드를 실행할 때마다 테스트 클래스 오브젝트를 새로 만든다는 점

이는 각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해주기 위해서 이다.

@Autowired
ApplicationContext applicationContext;

@BeforeEach
void setUp() {
    System.out.println(applicationContext);
    System.out.println(this);
}

JUnit 확장 기능은 테스트가 실행되기 전에 딱 한 번만 애플리케이션을 만들어두고 테스트 오브젝트가 만들어질 때마다 애플리케이션 콘텍스트를 테스트 오브젝트의 특정 필드에 주입해 준다.  일종의 DI로 볼 수 있지만 관계가 있는 DI라고 보기는 성격이 다르다.

 

Fixture

 테스트를 수행하는 데 필요한 정보나 오브젝트를 픽스처라고 한다. 이는 보통 @BeforeEach에 포함되어 있을 확률이 있다. 

 

 

테스트를 위한 애플리케이션 콘텍스트 관리

 

 스프링은 JUnit을 이용하는 테스트 컨텍스트 프레임워크를 제공한다. 이를 통해 간단한 애노테이션 설정만으로 테스트에서 필요로 하는 애플리케이션 콘텍스트를 만들어서 모든 테스트가 공유할 수 있게 할 수 있다.

 

@ExtendWith, @RunWith

 

JUnit 프레임워크의 테스트 실행 방법을 확장할 때 사용하는 어노테이션이다.  이는 테스트를 진행하는 중에 테스트가 사용할 애플리케이션 컨텍스트를 만들고 관리하는 작업을 해준다.

 

@ContextConfiguration 

 

자동으로 만들어줄 애플리케이션 콘텍스트의 설정 파일 위치를 지정할 수 있다,

 

테스트 클래스마다 다른 설정 파일을 사용하도록 만들어 줄 수 있고, 몇 개의 테스트에서만 다른 설정파일을 사용할 수도 있다. 

스프링은 설정 파일의 종류만큼 애플리케이션 콘텍스트를 만들고, 같은 설정 파일을 지정한 테스트에서는 이를 공유한다.

 

@SpringBootTest

 

스프링 부트에 대해 많은 것이 통합되어 있는 어노테이션이다. 내부 어노테이션에 @ExtendWith가 포함되어 있다. 

 

통합적인 테스트에 적합한 애노테이션이다. 실제 스프링 환경으로 테스트를 할 때 사용하면 될 것 같다.

 

@DirtiesContext 보다 테스트를 위한 application config를 사용하자.

 

 

* 침투적 기술과 비 침투적 기술

 

침투적(invasive) 기술은 기술을 적용했을 때 애플리케이션 코드에 기술 관련 API가 등장하거나, 특정 인터페이스나 클래스를 사용하도록 강제하는 기술을 말한다. 

 

침투적 기술을 사용하면 애플리케이션 코드가 해당 기술에 종속된다. 반면 비 침투적(noninvasive)인 기술을 애플리케이션 로직에 담은 코드에 아무런 영향을 주지 않고 적용이 가능한 기술을 말한다. 

 

스프링은 이런 비 침투적인 기술의 대표적인 예라고 할 수 있다. 

 

DI에 관해 어느 상황이 적절한지 가끔 고민하는 경우가 있다. 이 경우 효과적인 테스트를 만들기 위해서는 어떤 필요가 있는지 생각해보는 것이 좋다고 조언한다.

 

두 개의 클래스가 강 결합이 되어 있다면 이는 DI가 불가능한 구조로 테스트할 때 불편해지거나, 자동화된 테스트가 불가능할 수 있기 때문이다. 

 

좋은 코드의 조건은 많지만 테스트하기 좋은 코드가 좋은 코드일 가능성이 높다.

 

테스트를 하는 방식에는 여러 방식이 있었다. 스프링 컨테이너의 도움을 받는 경우, 컨테이너 없이 수동 DI를 통해 빠르게 테스트를 진행하는 경우..

 

복잡한 의존관계를 갖고 있다면 도움을 받고 그렇지 않은 경우 컨테이너 없이 테스트를 작성해보자.

 

 

Mail, S3..

 

개발 단계에서 이루려는 목적에 특정 서버나 스토리지가 필요한 경우 어떻게 해야 할까?

개발 단계에서 개발 메일서버와 개발 스토리지를 구축하여 테스트를 해야 할까?

구축 이후 테스트마다 메일을 보내고 S3에 파일을 저장하고 다운로드하는 것이 맞을까?

 

개발 중이거나 테스트를 수행할 때는 뭔가 대신할 수 있는 하지만 동일한 인터페이스를 갖는 코드가 필요하다. 이렇게 할 수 있다면 굳이 불필요한 요청을 보내지 않아도 되고 테스트 시간도 감축될 것이다.

 

우리는 대역이 필요하다. 동일한 인터페이스만 가지고 있을 뿐 동작을 하지 않는 대역 말이다.

 

대표적인 테스트 대역은 test stub이다. 테스트 스텁은 테스트 대상 오브젝트의 의존 객체로 존재하면서 테스트 동안에 코드가 정상적으로 수행할 수 있도록 돕는 것을 말한다. 

 

많을 경우 테스트 스텁이 결과를 돌려줘야 할 때도 있다. 호출만 하면 그만인 경우도 있지만 리턴 값이 있는 메서드를 이용하는 경우에는 결과가 필요하다. 

이럴 땐 스텁에 미리 테스트 중에 필요한 정보를 리턴해주도록 만들 수 있다. 또는 스텁에서 강제로 예외를 발생하여 예외 테스트도 진행할 수 있다.

 

만약 테스트 오브젝트가 의존 오브젝트에 값을 넘기는 값과 그 행위 자체에 대해 검증하고 싶다면 어떻게 해야 할까?

 

이런 경우 테스트 대상의 간접적인 출력 결과를 검증하고, 테스트 대상 오브젝트와 의존 오브젝트 사이에서 일어나는 일을 검증할 수 있도록 Mock Object를 사용해야 한다.

 

테스트는 테스트의 대상이 되는 오브젝트에 직접 입력 값을 제공하고, 테스트 오브젝트가 돌려주는 출력 값, 즉 리턴 값을 가지고 결과를 확인한다. 

 

테스트가 수행되는 동안 테스트 대상이 의존하고 있는 다른 의존 오브젝트와 협력하는 경우 다른 의존 오브젝트로부터 입력값은 필요하게 된다.

 

이를 위해 단순히 출력도 입력도 하지 않는 대역보다 목 오브젝트를 활용해 입력값을 받도록 만들어 사용하는 것도 추천한다.

 

목 오브젝트는 테스트 대상의 코드가 정상적으로 수행되도록 도와주고 추가적으로 부가적인 검증 기능까지 갖고 있는 걸 잊지 말자.  

단순히 테스트 대상의 코드 검증뿐만 아니라 목 오브젝트를 통해 발생하는 일에 대한 검증이 필요할 때 사용한다는 이야기다.

 

고립된 단위 테스트

 

단위 테스트를 만들기 위해서는 스텁이나 목 오브젝트의 사용이 필수적이다. 의존 관계가 없는 테스트는 대부분 세부 로직 검증이나 메서드 단위로 테스트할 때가 아니라면 대부분 의존 오브젝트를 필요로 하기 때문이다.

 

단위 테스트가 가장 우선시해야 할 테스트 방법인 건 맞지만 목 오브젝트를 만들어 주는 과정은 가장 큰 고민거리가 아닐 수 없다. 인터페이스에 따라 목 구현체는 전부 구현해주어야 할 수도 있기 때문이다.

 

다행히 이런 번거로운 목 오브젝트를 편리하게 작성하도록 도와주는 목 오브젝트 프레임워크가 있다.

 

Mockito 

 

Mockito를 통해 만들어진 목 오브젝트는 메서드의 호출과 관련된 모든 내용을 자동으로 저장해 두고, 이를 간단한 메서드로 검증할 수 있게 해 준다.

 

목 오브젝트는 다음의 네 단계를 거쳐서 사용하면 된다.

 

1. 인터페이스를 이용해 목 오브젝트를 만든다.

2. 목 오브젝트가 리턴할 값이 있으면 이를 지정해 준다.

3. 테스트 대상에 DI를 해서 목 오브젝트가 테스트 중에 사용되도록 만든다.

4. 테스트 대상 오브젝트 사용 후 목 오브젝트의 특정 메서드가 호출됐는지, 어떤 값을 가지고 몇 번 호출됐는지를 검증한다.

 

 

 

한번 머릿속에 테스트 관련 배경이 깔렸다. 

 

지금 나에게 필요한 건 어떤 테스트를 진행할 건지, 의존관계가 많은 경우 목 프레임워크를 활용해 단위 테스트로 빠르게 검증할 수 있다는 것과 Mockito를 통해 목 오브젝트의 행동을 검증할 방법을 공식 문서를 보고 습득하는 것뿐이다.

 

반응형

'Test' 카테고리의 다른 글

TDD init  (0) 2022.12.10
TDD 시작  (0) 2022.12.10
데이터베이스 연동 테스트  (0) 2022.10.14
JUnit5 (2)  (0) 2021.03.14
Test  (0) 2021.03.14

댓글