테스트코드를 경험해보며

Hyeon-Uk·2023년 7월 19일
0

끄적끄적

목록 보기
4/4
post-thumbnail

읽기전에....

이 글은 경험에서 우러나온 글이기 때문에 개인적인 의견이 존재할 수 있습니다.

TEST_CODE를 작성해야 하는 이유?

👀서버를 언제 또 띄우고,,,, 언제 직접 입력하고,,,, 언제 다 눈으로 비교해???

직접 서버를 구동시켜 테스트를돌리는 것은은 매우 번거롭고시간이 오래 걸리며며,매뉴얼이이 존재하지 않으면 테스트마다 다른 결과값을 가질 수 있기 때문에 비효율적이다.

그나마 Postman을 이용하여 케이스별로 상태를 저장할 수 있어 빠른 테스트를 할 수 있다는 기대가 있다.

하지만 이 방법 또한 테스트용 서버를 직접 구동시킨 뒤, Postman에서 직접 API를 호출한 뒤, 결과값을 두 눈으로 메뉴얼 상의 기댓값과 비교를 해야 한다.

사람의 눈으로 직접 결과를 보며 테스트하기 때문에 테스트의 기댓값이 나오지 않더라도 놓칠 수 있기 마련이다.

image

직접 서버를 띄우고,,,,,Postman을 켜서 데이터를 집어넣고,,,, SEND!
하지만 이게 내가 원하는 값이 맞나?!!?!?!?!?!?

테스트 코드 작성 시 기댓값을 테스트코드에 적어놓은 뒤, 결과값이 기댓값과 맞으면 Success, 틀리면 Fail로 나오기 때문에 두 눈으로 비교하지 않아도 JUnit라이브러리가 알아서 화면에 띄워준다.

v 표시가 떴다면 기댓값과 결과값이 일치한다는 의미이므로, 눈으로 비교하지 않아도 된다.
image

🙏코드를 개발/리팩토링했는데,,,, 이게 잘 될까!?!?!? 기도하자!

코드를 개발 및 리팩토링을 진행하면서 많은 걱정이 존재할 것이다. "내 코드가 진짜 잘 돌아갈까?" , "리팩토링했다가 로직이 꼬여버리면 어쩌지?!" , "이 라이브러리를 사용하는 것보다 다른 라이브러리를 사용하는 게 더 효율적이야! 근데 이걸 적용하다가 또 로직이 꼬여버리면 어쩌지!?" 등의 많은 고민이 있을 것이다.

실제로 첫 프로젝트를 진행하며 테스트코드를 작성하지 않았던 탓에 서비스 확장 및 리팩토링을 하기 너무 두려웠던 경험이 있다. 또한 이런 불안함을 가지고 배포까지 진행했던 프로젝트기 때문에 언제 오류가 터져 서버가 다운되어도 모르는 불안정한 서버인 채로 배포되었다.

이런 불안감을 완벽한 테스트코드 한 번으로 떨쳐낼 수 있으니 얼마나 좋은가?

🙅‍♂️오류가 나는 코드는 입구컷!

불안감을 갖고 완성한 코드를 배포한 뒤, 클라이언트를 맡은 친구에게 "서버 올렸어! 더미 데이터 말고 이제 직접 통신 잘 되는지 확인해 보자!"라고 말한 지 얼마 지나지 않자 제게 온 카톡 "뭐 잠깐 연동해서 테스트했는데 서버가 다운됐는데? 확인 부탁해!"

맙소사 역시 이럴 줄 알았어! 그렇게 밤을 꼬박 지새우며 디버깅하다 일부 설정파일이 잘못된 것을 발견하여 고쳐 다시 배포.....

테스트코드가 있었다면 이를 미리 발견할 수 있지 않았을까!?

이 모든 잡담을 정리하자면 다음과 같다.

테스트코드의 장점

  1. 직접 서버를 구동시켜 메뉴얼대로 하나하나 클릭하지 않아도 된다.

    • 이는 Junit 라이브러리가 직접 코드를 실행시키면서 기댓값을 Return하는지 검증해 주기 때문에 버튼 한 번의 클릭으로 테스트가 가능해진다.
  2. 각 레이어별 단위 테스트를 작성하게 된다면, 개발 단계에서 어느 레이어에서 문제가 발생했는지 한눈에 알기 쉽다.

    • 직접 서버를 구동시켜서 테스트를 진행하다가 에러가 발생했을 때, 이 에러가 어느 레이어에서 발생했는지 찾기 위해서 모든 레이어를 검사해야 한다.
    • 테스트코드를 레이어별로 작성했을 때 Junit이 기댓값이 아닌 다른 값을 return받으면 IDE상에서, 혹은 빌드 중 콘솔에서 Fail을 띄워준다. 문제가 발생한 Layer, 혹은 모듈을 손쉽게 확인가능하기 때문에 디버깅에도 간편해진다.
  3. 통합테스트까지 완료를 한다면 과감한 확장이 가능해진다.

    • 테스트코드가 컴팩트하게 짜였다면 리팩토링과 서비스 확장이 과감해진다.
    • 테스트코드가 존재하지 않으면 리팩토링하면서 "버그가 터지진 않을까"라는 불안감을 가지고 개발 및 배포를 진행해야 한다.
    • 테스트코드가 존재한다면 과감하게 리팩토링한 후 작성된 테스트코드를 돌려서 실패하면 기존의 서비스와는 다른 결과값을 Return한다는 것을 쉽게 알 수 있기 때문에 리팩토링을 과감하게 할 수 있다.
    • 또한 서비스를 확장, 라이브러리/모듈 변경 시 테스트코드를 돌려봄으로써 해당 부품이 서비스에 잘 맞춰 돌아가는 것을 쉽게 검증할 수 있다.
  4. 빌드 시 테스트코드를 통해 안정적인 서버를 배포할 수 있다.

    • 서버를 배포하기 전, Build과정에서 테스트를 진행할 수 있는데, 테스트를 실패한다면 빌드과정에서 오류가 발생하여 취소된다.
    • 따라서 테스트코드를 통과하지 못하는, 버그가 있는 서버를 배포하기전에 한번 검사를 할 수 있다.

테스트코드의 단점

  1. 번거롭다

    • 모든 상황에 대한 테스트케이스(성공, 실패, 예외처리 등)를 모두 작성해야하기 때문에 설계단계에서 상세한 기능구현을 설계하지 않으면 테스트케이스를 생각해내는 과정 및 작성하는 과정이 매우 번거로워질 수 있다.
  2. 컴팩트하지 않은 테스트코드는 잘못하면 독이 될 수 있다.

    • 설계과정에서 모든 경우를 고려하지 않은 상태에서, 혹은 테스트케이스를 즉흥적으로 작성하다보면 놓치는 예외케이스들이 존재할 수 있다. (아무래도 사람이다 보니 실수를 할 수 있기에 설계과정에서 팀원들과 함께 의견을 나누며 정리해야 한다.)
    • 크리티컬한 예외케이스를 놓친 뒤 테스트케이스를 작성하고 "난 모든 테스트케이스에 대한 코드를 작성했으니 안정적인 서버가 구동될거야!" 라고 생각하고 과감한 배포를 진행할 수 있다.
    • 따라서 설계 및 코드리뷰 과정에서 꼼꼼하게 빠진 케이스가 없는지 확인하는 절차를 거쳐야 한다.
  3. 느린 배포시간

    • 테스트케이스를 비효율적으로 짜게되면 테스트코드를 실행시키는데 걸리는 시간이 매우 오래걸리게된다.
    • 이는 빌드 전 테스트코드를 실행시키는 과정에서도 오래걸리게 된다.
    • 빌드가 오래걸리게 되면, 배포또한 오래걸리기 때문에 테스트코드도 효율적으로 짜야한다.
  4. 리팩토링

    • 서비스의 요구사항이 바뀌게 되면 서비스 코드가 변경되기 마련이다.
    • 이런 변경사항에 맞춰서 테스트코드 또한 리팩토링해야 한다.

테스트코드 작성 원칙

  1. Fast : 빠르게 실행되어야 한다.
  2. Isolated : 각 테스트코드는 독립적으로 실행되어야 한다.
  3. Repeatable : 매번 실행결과는 동일해야 한다.
  4. Self-Verifying : 결과는 Success Or Fail 의 값을 가져야한다.
  5. Timely : 테스트코드를 먼저 작성해야한다.

테스트코드 작성 패턴

Given / When / Then 패턴을 주로 사용한다.

  • Given

    • 테스트를 수행할 데이터를 준비하는 부분이다.
  • When

    • 테스트를 할 상황을 시뮬레이션하는 부분이다.
  • Then

    • 테스트를 수행한 뒤 결과를 검증하는 부분이다.

테스트 종류

  • 단위테스트

    • 단위별로 테스트를 하여 검증하는 방법이다.
    • 단위별로 테스트를 하다보니 디버깅이 편하다.
  • 통합테스트

    • 여러 모듈, 레이어를 협력했을 때 잘 동작하는지를 검증하는 테스트
    • 협력상태에서 검증을 하기 때문에 어느부분에서 오류가 발생했는지 디버깅이 힘들다.

Mocking

단위테스트는 해당 단위의 기능을 검증하기 위한 테스트이다.
예를들어 Controller - Service - Repository 구조에서 Service레이어를 테스트하기 위해 아래와 같은 테스트코드를 작성했다면 단위테스트일까?

@Test
public void saveSuccessTest(){
    boolean result = memberService.save(대충 알맞은 DTO객체);

    assertThat(result).isTrue();
}

답은 아니다. 오롯이 서비스 레이어만 테스트를 진행한게 아닌, 의존관계를 맺고있는 실제 DB커넥션을 하고있는 Repository레이어까지 함께 검증을 하게된것이다. 따라서 테스트용 DB설정정보가 잘못되거나, 테이블이 잘못되어 오류가 발생하게 되면, 위의 테스트코드는 올바른 테스트코드라고 할지라도 실패를 하게된다.

따라서 해당 레이어만을 테스트하고 싶으면, 연관관계를 맺고있는 Repository레이어를 Mocking시켜 DI시킨다면, 실제 DB에 커넥션을 맺는 과정이 아닌, 개발자가 커스텀한 로직을 따르도록하며 DB와의 종속관계를 끊어버릴 수 있게된다.

profile
Hi🖐

0개의 댓글