Rlog

테스트를 어떻게 해야하는가? 본문

Test

테스트를 어떻게 해야하는가?

dev_roach 2021. 10. 27. 19:24
728x90

개요

최근 어떤 테스트가 좋은 테스트인지? 무엇을 테스트 해야 하는건지에 대한 의문점이 생겼다.
그래서 springcamp.io 에서 발표한 용근님의 발표내용을 정리해보려고 한다.

 

https://www.youtube.com/watch?v=YdtknE_yPk4&t=174

테스트로 얻을 수 있는 것

 

제일 중요한것은 안정감과  자신감의 상승. 
안정감과 자신감은 미래의 나 그리고 현재의 나의 동료들이 느낄 수 있도록 테스트 코드를 작성해야 한다.

무엇을 테스트 할 것인가

예시코드는 로또였는데, 처음에는 로또가 6개를 반환하는 것만 테스트 되어 있었음.

요구사항은 아래와 같은 세가지였다.

- 중복 처리
- 순서가 잘 바뀌었는지
- 6개의 수를 반환하였는지

처음에는 작성자가 Set 으로 구현했기 때문에 중복처리 테스트를 안했지만, 이는 잘못된 것이다.
언젠가는 구현체가 List 로 변할 수도 있는데 그 때 테스트를 다시 작성해야 할까? 혹은 리팩토링을 하는이가 테스트코드를 적어야 할까?

용근님 발표자료


구현이 아니고 비즈니스적인 설계 테스트를 이뤄내야 함 예를 들면 위에 요구사항을 기반으로 테스트를 하는것.

우리가 작성한 코드가 테스트 코드가 되는게 아니고 설계를 했던 사항이 테스트코드로 작성되어야 함.


Non-Testable


우리가 제어할 수 없는 영역, 예를 들면 랜덤메소드나 외부에서 성공 / 실패에 대한 응답을 받는 것들(PG 사 결제 등등)

왜 그럼 외부세계는 테스트를 할 수 없을까?

그 이유는 외부세계의 장애같은 것이 내 테스트에 영향을 끼친다. 즉 일정한 케이스가 아님. (멱등성을 위반함)

항상 성공할 수 있는것, 항상 동일한 결과가 나올 수 있는 것을 테스트 해야한다.

용근님 발표자료

위와 같이 배달팁을 계산할때는 모든것이 Testable 해 보인다.

하지만 코드를 파고들다 보면 아래와 같은 Non-Testable 한 로직이 나온다.

용근님 발표자료

왜 Non-Testable 하다고 말할까? 

우리는 LocalDateTime.now() 라는 함수의 시간을 제어할 수 없기 때문이다. 

따라서 테스트 트리는 아래와 같이 전부 오염되고 만다.

용근님 발표자료

이것을 어떻게 Boundary Layer 까지 끌어올릴 수 있을까?

용근님 발표자료

용근님은 아래 사진과 같이 우리가 시간에 대한 의존성을 밖에서 주입할 수 있는 구조로 탈바꿈했다.

용근님 발표자료

즉 위의 사진과 같이 calculateDeliveryTip 가 Boundary Layer 가 되는 것 이다.

 

Spring 에서 테스트를 한다면?

우리가 로또 로직을 웹으로 작성해야 한다고 해보자. 

그렇다면 로또 로직을 Bean 으로 만들려고 할것이다. 예를 들면 아래 사진과 같이 말이다.

그런데 여기서 한가지 의문이 있다.

과연 위와 같이 SpringContext 를 주입받고 테스트를 해야하는 로직인가? 라는 것이다.

 

Spring 을 많이 사용해봤으면 알겠지만, 초반 Spring Context 를 만들기 위한 큰 비용이 들게 된다.

코드를 고치고 테스트를 하는 싸이클에서는 빠른 피드백이 불가능 하다는 소리다.

즉속도차이도 업무 효율에 상당한 영향을 끼친다.

 

용근님께서 위와 같은 말을 해주셨는데, 그 이유는 무엇일까? 

예를 들면 아래와 같은 코드를 한번 보자.

private ShuffleStarategy shuffleStarategy

위와 같은 코드는 final 이 아니므로 자바에서는 필수적이지는 않다.

실상 Nullable 처럼 이용될 수도 있다는 뜻이다.

 

그럼 Spring 에서 이 필드를 한번보자

@Autowired
private ShuffleStarategy shuffleStarategy

Spring 에서는 어떨까? 

Bean 을 주입시키라고 명령하는 뜻이다.  즉 Java 로 친다면 final 이 있어야 할 것 같다. (필수 의존성)

그래서 Spring 버전이 높아지고 나서부터는 Constructor Injection 을 권장하기는 한다. 아래와 같이 말이다.

private final ShuffleStarategy shuffleStarategy

public Lotto(ShuffleStarategy shuffleStarategy) {
	this.shuffleStarategy = shuffleStarategy;
}

위와 같이 Java 와의 Context 를 이제 맞출 수 있게 되었다.

Context 나 FrameWork 에 종속적이지 않은 테스트를 작성해야 한다.

Test Double

테스트 더블이란? 테스트 할 수 없는 영역에 대한 외부 요인을 부여할 수 있도록 도와줌

자바 진영에서 가장 많이 쓰이는 것은 Mockito 를 통한 Mocking 을 이용하는 것이다.

 

"무엇을 테스트 더블로 처리해야할까?"

 

위의 진행 Flow 를 보면 우리가 테스트해야할 영역은 4번 영역이다.

이럴 때 우리가 테스트 더블로 해야할 영역은 상식적으로 생각해봤을때는 아래와 같다고 말할 수 있을 것이다.

이렇게 됬을때는 문제는 결국 4번에서 구현을 알아야 한다는 점이다.

 

만약 2번에서 반환타입을 변경하게 된다면 엄청난 에러가 발생되게 될 것이다. 

쉽게 얘기하면 1,2,3 을 모두 모킹했을때 1,2,3 의 변경에 따른 모킹리턴이나 모킹입력등등이 계속해서 변경된다는 것이다.

 

지금 내 사이드 프로젝트에서 겪고 있는 문제다..

이 부분에서 테스트를 포기하는 사람이 많아진다고 말한다.

테스트 더블은 사용자체갸 잘못됬다기 보다는 남용됬을때 이런 문제가 발생할 수 있다는 것이다.

그럼 테스트 더블은 어떻게 사용해야 할까..? 

 

용근님은 아래와 같이 하나의 원칙을 가지고 하신다고 한다.

2번 3번과 같은 모듈은 이미 많은 테스트가 되었다면 이를 통합테스트로 도출하려고 하며,

1번에만 테스트 더블을 이용하신다고 한다.

 

뎁스가 1 -> 2 -> 3  -> 4 로 이어진다고 했을때 기준이다.

계속해서 하시는 말씀은 구현위주의 테스트로 들어갔을때 많은 문제가 발생할 수 있다고 말씀하시는 것 같았다.

Embedded

위와 같은 Embedded 를 이용한다면, 우리가 제어할 수 없는 영역들을 제어 가능하게 만들 수 있다.

 

다만 Embedded 를 이용할 경우에는 테스트 싸이클을 잘 설정해야 한다.

Embedded 는 테스트 주기 안에서만 생존하는 구조여야 한다.

EndPoint Test

주로 우리가 EndPoint Test 를 진행할때는 아래와 같은 라이브러리를 이용한다.

위와 같은 라이브러리들은 우리가 조금 더 EndPoint Test 를 쉽게 하도록 도와준다.

원래 보통의 E2E Test 는 아래와 같이 모든 로직을 전부 타는 방식으로 진행하는 경우가 많다. (요것또한 내생각일수도)

위와 같은 방식으로 진행했을때 내가 정한 테스트 케이스에서는 성공여부를 정확히 알 수 있지만, 오래 걸린다는 단점이 있다.

또한 깊은 곳에 우리가 제어하지 못하는 코드들이 존재할 수도 있다.

그래서 용근님은 이게 스트레스 일수도 있으니 아래와 같은 방식으로 진행하는 경우가 있다고 했다.

그래서 용근님은 위와 같은 방법으로 타협하셨다고 한다.

그래서 위의 테스트는 요청과 응답에 대한 값만 검증하며, 뒤의 로직은 정상적으로 동작한다 라는 보장하에 이루어진다.

 

이렇게 했을때 정신건강에 상당히 좋다고 한다.

다만 그렇다고 전부 통합테스트를 이렇게 하라가 아니라, 내 생각에는 응답과 요청을 검증하는 테스트에서는 위와 같은 방식이 정신건강에 좋을 수 있다는 것으로 생각하는 것이 올바른것 같다.

 

RestDocs 를 함께 이용하면 Test 와 함께 문서를 작성할 수 있으므로 잘 이용하자!

정리하며

결국 용근님이 얘기하신것에 중요한 것은 테스트란 프로덕 품질의 신뢰성일 수도 있지만

이 코드를 작성하는 나 혹은 팀원이 얼마나 신뢰성과 안정성을 느끼게 해주는 것이라고 말씀하시는 것 같았다.

 

나 또한 테스트가 리팩토링이나 코드를 작성하는데 중요하는 것을 알고 있으나

가끔 로직하나를 수정하는데 여러 테스트 코드를 고치게 되면 스트레스를 받을 때가 많다.

 

또한 느낀것은 우리는 자바 개발자다 라고 말한것이 기억에 남는다.

최대한 Context 나 FrameWork 에 의존하지 않도록 테스트를 작성해야 겠다는 생각이 많이 들었다.

 

마지막으로 용근님이 주시는 팁을 남기며 포스팅을 마무리하려고 한다.

그럼 다들 좋은 테스트 코드를 작성하길 바라며!

 

 

'Test' 카테고리의 다른 글

[TEST] When 에서 하나 이상의 메소드 실행 피하기  (1) 2021.12.17
테스트 더블의 종류  (0) 2021.11.22
테스트 더블  (0) 2021.11.19
어떤 것이 좋은 테스트 코드인가?  (0) 2021.11.19