테스트 코드?

: 내가 작성한 메서드가 실제로 잘 동작하는지 확인하는 용도로 작성하는 코드를 일컫는다.

 

이걸 왜 쓰는가?

: 궁극적으론 기능이 정상적으로 동작하는지 확인하기 위함. 기존에 내가 하던 방식은 중간중간 print를 찍어보는 거였지만, 테스트 코드를 통해서는 보다 편하게 내가 만드는 기능이 정상적으로 동작하는지 확인할 수 있음.

 또한, 결함이 생기면 사전에 발견 가능함. 기존에 여러 가지 기능에 대한 테스트 코드를 짜뒀다면 추후 기존 코드를 잘못 작성했을 때 테스트 과정에서 오류가 생기므로 예기치 못한 결함을 사전에 발견하고 조치할 수 있음

 또한 테스트 코드 자체를 문서로써 활용 가능함. Because 작성자의 의도, 사용법, 주의사항 등이 테스트 코드를 통해 드러나는 것과 마찬가지기 때문!

 또한, 테스트를 쉽게 하기 위해 프로덕션 코드를 테스트하기 쉽게 짜는 과정에서 프로덕션 코드가 깔끔하게 만들어지는 것도 테스트 코드를 쓰는 이유 중 하나다.

 

(참고: 프로덕션 코드란 프로그램 구현을 담당하는 부분으로 사용자가 실제로 사용하는 소스 코드를 말함. 테스트 코드는 이 프로덕션 코드가 실제로 잘 돌아가는지를 확인하는 코드임)

 

그냥 Main메서드에서 print로 중간중간에 찍어보면 되잖아? 굳이..?

: 물론 그것도 일종의 테스트라곤 볼 수 있음. 그러나 이건

  1. 프로덕션 코드와 테스트 코드가 함께 존재
  2. 테스트 코드가 실제 배포 시 같이 배포되므로 따로 배포됨. 
  3. Main에서 여러 가지를 테스트하는 것이므로 복잡도가 증가
  4. 코드상에서 보면 단순 print만 쓰니까 어떤 걸 테스트하는지 파악하기 힘듦
  5. 테스트 결과를 사람이 직접 눈으로 확인해야 함(ex: 음 여기선 A가 나와야하는데 출력결과를 보니까 A가 맞군.. 등)

등등의 문제들이 있다.

이런 것들을 테스트 코드를 통해서 해결할 수 있다는 얘기!!

 

참고 - TDD?

: Test Driven Development의 줄임말.

"테스트 주도 개발"이라고 하며 설계 이후 프로덕션 코드를 짜고 테스트를 하던 기존의 방식과는 달리, 설계 이후에

  1. 실패하는 테스트 코드 작성
  2. 테스트가 통과하는 프로덕션 코드 작성
  3. 테스트가 통과하면 프로덕션 코드를 리팩토링

하는 방식으로 개발하는 걸 말한다.

 

테스트 피라미드

  • Unit Test: 말 그대로 단위 딱 하나(함수, 모듈 클래스..)를 테스트하는 것. 자동차라는 '전체'에서 바퀴 하나라는 '단위'를 테스트한다면 단위 테스트라 볼 수 있음
  • Integration Test: 통합 테스트를 의미. 하나의 단위에서 더 나아가서 여러 가지 단위를 통합했을 때 서로 잘 돌아가는지 테스트하는 것. 자동차에서 바퀴와 모터 등을 연결하여 바퀴을 돌릴 때 바퀴가 잘 돌아가는지와 같은 서로 간의의 상호작용을 확인하는 테스트
  • E2E Test: End-to-End의 줄임말. 실제로 유저가 그 앱을 사용할 때의 플로우를 테스트하는 것. 운전대를 잡고 악셀을 밟으면서 자동차가 잘 나가는지를 테스트하는 거라 보면 된다

Junit5? 

: 현재 전 세계적으로 가장 널리 쓰이는 Java의 단위 테스트 프레임워크. Annotation을 활용해 가독성 좋은 테스트 코드를 작성할 수 있게 해주는 도구라고 보면 된다.

 

(참고: Annotation이란 자바에서 일종의 주석이란 의미를 가지는 녀석으로, 코드 앞에 @를 붙여서 사용해준다. 이를 통해 코드 사이에 주석처럼 쓰여서 특별한 의미를 부여하거나 특별한 기능을 수행하도록 만들어줄 수 있다. 즉 프로그램에 추가적인 정보를 전달하는 용도로 쓰이는 일종의 메타데이터라고 보면 된다!)

 

그런데 이때, Junit만으로도 단위 테스트를 충분히 작성할 수 있긴 하지만 Junit에서 제공하는 메소드들이 가독성이 많이 떨어져서 Assertj와 조합하여 많이 사용한다!

 

Assertj?

: Java 테스트를 돕기 위한 다양한 문법을 지원하는 라이브러리

 

 

하는 방법

일단, gradle로 자바 프로젝트를 만들면 junit은 알아서 설치돼있다. 여기서 assertj를 깔아줘야 함.

gradle로 프로젝트를 관리하는 경우, 외부 라이브러리같은 걸 추가하고 싶으면 build.gradle파일의 dependency목록에 원한는 라이브러리를 작성후 코끼리 버튼을 눌러 설치해야 한다고 한다...! 이거 뭐가 뭔지 몰라서 2시간 넘게 헤멨다..하..코끼리가 귀여우니 봐줌^-^ 아니었음 넌 뒤졌다 ㅎㅎ

 

하단 코드를 build.gradle에 작성해줍니당

testImplementation "org.assertj:assertj-core:3.20.2"

그리고 우측 상단(인텔리제이 기준)에 나오는 코끼리 눌러서 저걸 깔아줌. 

이후, 다음과 같이 import함으로써 assert의 기능들을 사용 가능..!

import static org.assertj.core.api.Assertions.*;

 

(참고: 의존하는 라이브러리를 가져올 때, compile로 가져올 수도 있고 implementation으로 가져올 수도 있다고 한다. 이 때 compile 은 상위 모듈까지 가져오는 (B를 가져오고 싶은데 B가 A에 의존한다면 A도 가져오는 것) 거고 implementation은 딱 내가 원하는 고 놈만 가져오는 거라고 한다. 이 때 test를 앞에 붙인 건 프로덕션코드가 아닌 테스트코드를 수행할 때만 적용한다는 의미!)

 

 

테스트 코드 작성 시 규칙

@Test
@DisplayName("~~에 관한 테스트")
void inputValidTest() {
    // given
    // when
    // then
}
  • @Test: 해당 메소드가 단위 테스트임을 명시하는 Annotation. Junit은 테스트 패키지 하위의 @Test Annotation이 붙은 메서드를 단위 테스트로 인식하여 실행시킴!
  • @DisplayName: 단순히 @Test가 붙은 메서드를 실행하면 테스트 이름이 메서드 이름이 되는데, @DisplayName Annotation을 통해 테스트 이름을 내가 원하는 걸로 부여할 수 있음

 

테스트코드의 작성 방법 - given/when/then 패턴으로 작성한다.

: 1개의 단위 테스트를 3가지 단계로 나누어 처리하는 패턴을 말함

  • given(준비): 어떤 데이터가 준비됐을 때
  • when(실행): 어떤 함수를 실행하면
  • then(검증): 어떤 결과가 나와야 한다

 

예시

@Test
void stringTest(){
    assertThat("League of Legend").isNotNull()
            .startsWith("League")
            .contains("of")
            .endsWith("Legend");
}

"League of Legend"라는 문자열이 null이 아닌지, League로 시작하는지, of를 포함하는지, Legend로 끝나는지를 테스트!!

        @Test
        void 둘이_같니() {
            int actualAddResult = Main.adder(1, 2);
            int expectedAddResult = 3;
            assertThat(actualAddResult).isEqualTo(expectedAddResult);
        }

actualAddResult와 expoectedAddResult가 같은지 테스트!!

 

테스트 코드 실행시 실행하는 메서드이름이 보이게 하려면..

세팅에 들어가서 Run tests using을 인텔리제이로 바꿔줘야 함.

 

그러면 기존에는

이런 식으로 3가지 없게 나오던 녀석이

요로코롬 메서드이름도 예쁘게 보여준다..

+ Recent posts