인프런 커뮤니티 질문&답변

요니님의 프로필 이미지

작성한 질문수

Practical Testing: 실용적인 테스트 가이드

Business Layer 테스트 (1)

테스트를 위하여 , OrderService의 createOrder의 파라미터로 registerDateTime을 추가한 측면

23.08.03 17:38 작성

·

376

-1

안녕하세요! 먼저 항상 좋은 강의 감사드립니다!\

다름이 아니라,

저는 제목대로 , 테스트를 위해서 OrderSerivce의 createOrder의 파라미터로 registerDateTime을 파라미터로 받게 추가한 측면이 개인적으로 적절하지 않다고 생각하여 질문글을 작성하였습니다.

파라미터를 사용하는 이유는 결국 외부로 부터 값을 받는다는 전제가 깔려있다고 생각하고,

이런 측면에서 보았을 때 요청값으로 시간 값을 받는다고 생각할 수 있습니다.

그렇게 생각을 했을때 개인적으로 2가지 정도의 의아한 점이 발생한다고 생각합니다.

  1. 클라이언트로 부터 넘겨받는 시간이 과연 등록 시간이라고 할 수 있는가? (network delay가 있을것 이기 때문)

  2. 그렇다고 Controller에서 now() 를 호출한 시간이라는 일종의 고정값을 받을거면 - 파라미터를 선언하는 의미가 있는가?

결론적으로 저는 createOrder의 파라미터로 registerDateTime을 선언하는것이 적합하지 않다고 생각합니다.

하지만 우리의 경우는 tdd로써 테스트를 위해 외부로 값을 추출하였는데 - 이러한 문제가 발생하였으므로, tdd 개발론이 과연 적절한 production code를 만드는게 기여하는가? 라는 측면에서 의문이 듭니다.

나아가 당연히 저의 미숙한 탓 이겠지만, 강의를 진행해주신 방식대로 온전한 비즈니스 로직을 작성하지 않고 , 테스트 - 개발 - 테스트 - 개발... 이런 플로우로 개발을 하는것이 과연 도움이 되는가? 도 조금 의아한 것 같습니다.

어쨌든 여기까지는 저의 순수한 개인적 생각인데요,

이런 부분에 대해서 강사님 께서는 어떻게 생각하시는지 말씀해주시면 정말 감사하겠습니다!

 

답변 2

2

박우빈님의 프로필 이미지
박우빈
지식공유자

2023. 08. 06. 17:18

안녕하세요, khd1692님! :)
하나씩 답변 드려 보겠습니다. ㅎㅎ

일단 '테스트 코드를 작성하기 용이한 구조를 만드는 것' 이전에, 프로덕션 코드의 설계 형태가 과연 적절한가, 에 대한 질문을 주신 것으로 이해하였습니다.

 

1. 클라이언트로 부터 넘겨받는 시간이 과연 등록 시간이라고 할 수 있는가? (network delay가 있을것 이기 때문)

가장 먼저, 제가 가정한 프로덕트에서 저는 클라이언트로 부터 넘겨받는 시간값을 주문등록 시간으로 사용하지는 않았습니다.
네트워크 딜레이를 말씀 주셨는데, 해당 상황이 발생하려면 Controller Request에 registerDateTime을 선언하여 해당 요청을 주는 키오스크 앱/웹 쪽에서 받았어야 할 것인데요.
이는 클라이언트에서 잘못된 값(ex. 시간대 조작)을 넘겨준다면 서버에서 검증할 방법이 없기 때문에 애초에 고려하지 않았고, 그렇기에 서버 단에서 등록시간을 발행해야 한다고 판단(가정)하였고, 자연스레 프로젝트의 가장 바깥 레이어인 Controller에서 현재시간값을 생성하여 사용한 것이었습니다. ㅎㅎ

 

2. 그렇다고 Controller에서 now() 를 호출한 시간이라는 일종의 고정값을 받을거면 - 파라미터를 선언하는 의미가 있는가?

두 번째로, 파라미터가 고정값이라서 파라미터 자체에 의미가 있는지를 말씀 주셨는데요.
사실 LocalDateTime.now()는 고정값이 아닙니다.
호출할 때마다 달라지는 값이죠. ㅎㅎ

(등록시간이라는 의미를 잠시 내려두고) 만약 '2023-08-06 09:00:00' 이라는 정해진 시간값을 항상 받는 구조였다면, 당연히 파라미터화 할 이유가 없을 것입니다.
적절한 곳에 적절한 이름을 붙인 상수를 선언하여 사용하고, 테스트 모듈에서도 그에 맞추어 테스트 코드를 비교적 쉽게 작성했을 것입니다.
그러나 우리가 이 등록시간이라는 정보를 외부로 분리한 것은, 이 코드가 관측할 때마다 다른 값이 확정되는 코드이기 때문입니다.
다시 말해서, 우리가 제어할 수 없는 값 이라는 범주에 들어가는 코드라는 의미입니다.
강의 중에도 언급드렸지만, 제어할 수 없는 값이라는 것은 특정 환경(given)을 가정하여 세팅한 뒤 어떤 변화(when)를 주었을 경우를 테스트해야 하는 상황에서 테스트를 아주 어렵게 만드는 요인 중 하나입니다.
관측할 때마다 달라지니 given절을 특정할 수가 없기 때문입니다.

또 다른 관점에서, 같은 등록 로직을 작성한다고 했을 때, Controller에서 발행한 now()보다 실제 주문을 생성하는 안쪽 코드에서 발행하는 now()가 더 자연스러운 프로덕션 코드가 아닌가, 라고 보실 지도 모르겠습니다.
하지만 저는 두 방식에 큰 차이가 있다고 생각하지 않습니다.
물론 주문 객체를 생성할 때 now()를 관측하는 것이 가장 자연스럽고 정확한 문맥임에는 동의하고 있으나, 테스트가 어려운 구조를 유지하면서까지 고수할만한 것인가 싶은 생각이고요. 일종의 Trade-off 라고 생각해볼 수 있을 것 같아요.
(저는 좋아하지 않고 권장하지 않지만 LocalDate.now()를 mocking해버리는 아주 powerful한 선택지가 있기는 합니다...ㅎㅎ)
컨트롤러 레이어에서 서비스 레이어에 요청하는 각각의 입장을 글로 풀어보자면 다음과 같은 느낌일 것 같아요.

  • 나 이런이런 정보로 주문을 만들건데, 주문 요청은 이 시간에 했어!

  • 나 이런이런 정보로 주문을 만들거야. 주문 요청 시간은 네가 알아서 판단하겠지?

이야기가 길어졌지만, 주문 등록(요청) 시간을 판단하는 주체의 차이라고도 볼 수 있을 것 같고요.
자연스러움을 조금 포기하더라도 테스트 가능한 구조로 만들어서 테스트 가능한 영역을 최대화하는 것이 더 이익이라고 생각하고 있습니다. ㅎㅎ

조금 다른 이야기지만, 단순히 메서드 시그니처 파라미터에 정해져 있는 코드(시간값)가 들어오는 것을 없애고 싶으시다면, 현재 시간을 발행하여 제공하는 Request scope bean 등을 만들어 주입받아 사용하는 방법도 고려해볼 수 있습니다. ㅎㅎ (주입 방식에 대한 차이겠지만요)

 

나아가 당연히 저의 미숙한 탓 이겠지만, 강의를 진행해주신 방식대로 온전한 비즈니스 로직을 작성하지 않고 , 테스트 - 개발 - 테스트 - 개발... 이런 플로우로 개발을 하는것이 과연 도움이 되는가? 도 조금 의아한 것 같습니다.

마지막 강의에서였나 이야기 한 것 같기도 한데, TDD도 저는 일종의 도구라고 생각합니다.
강의 중에 예시로 만든 프로젝트는 비교적 실무에 비해 요구사항이 간단하니 크게 차이를 못느끼셨을 수도 있을 것 같아요.
저는 비즈니스 요구사항이 복잡할수록 TDD가 빛을 발한다고 생각합니다.
프로덕션 코드를 우선하여 작성하였을 때, 인간의 뇌는 해당 요구사항이 정상적으로 작동하는 해피 케이스에만 집중하게 됩니다.
TDD는 생각하지 못한 테스트 케이스(TC)들을 먼저 생각하게, 그리고 발견하게 해주기 때문에 유용한 도구라고 생각하고 있어요.
(반대로 요구사항이 간단한 수준에서는 TDD를 사용할 때도, 사용하지 않을 때도 있습니다. 사용하기 좋은 도구일 뿐이니까요.)

 

제 생각을 최대한 텍스트 기반으로 전달하려다 보니 말이 길어졌네요.
제 글을 읽으시더라도 동의하지 않으시는 부분이 있을 수도 있습니다.
설계와 개발에 정답이 있는 것은 아니니, 저는 이러한 질답을 나눴다는 것에 충분한 의의가 있다고 생각합니다.

좋은 질문 주셔서 감사드립니다. ☺️

0

인프런 AI 인턴님의 프로필 이미지

2023. 08. 05. 17:41

안녕하세요, 인프런 AI 인턴이에요.

OrderService의 createOrder 메소드의 파라미터로 registerDateTime을 추가하는 것에 대한 의문을 가지셨군요. 파라미터를 사용하는 이유는 외부로부터 값을 받는다는 전제가 깔려있다고 생각하는데, registerDateTime은 클라이언트로부터 넘겨받는 시간으로 등록 시간이라고 말할 수 있는지, 또는 Controller에서 now()를 호출한 시간인 고정값을 받을 수 있을지에 대한 의문을 가지고 계신 것 같네요.

registerDateTime을 파라미터로 받는 방식은 TDD 개발론에서 테스트를 위해 외부로 값을 추출하기 위해 사용된 것입니다. 그럼에도 불구하고 등록 시간에 대한 의문이 발생했다면, registerDateTime 대신에 다른 방식을 고려해 볼 수도 있습니다. 예를 들어, createOrder 메소드 실행 시점에 현재 시간을 얻어오는 방법을 사용하거나, registerDateTime을 아예 로직 안에서 처리하는 방식을 고려해 볼 수도 있습니다.

TDD를 따르는 것은 개발할 때 코드의 품질을 높이고 유지 보수를 용이하게 하기 위한 방법 중 하나입니다. 하지만 언제나 상황에 따라 유연하게 접근해야 하며, 적합한 방식을 선택하는 것이 중요합니다. 따라서, 저는 createOrder의 파라미터로 registerDateTime을 선언하는 것이 적합하지 않다고 단정지을 수 없지만, 다른 유연한 방법을 찾아보는 것도 좋은 접근이 될 수 있을 것 같습니다.

더 궁금한 점이 있으시면 언제든지 물어보세요. 도움이 될 수 있도록 최대한 답변드리겠습니다. 감사합니다.

요니님의 프로필 이미지

작성한 질문수

질문하기