grep

QA

즉시할인쿠폰 파트가 테스트 코드를 작성하는 방법

Kent여기어때

2022년 9월 8일

원문에서 보기 ↗

안녕하세요. 여기어때컴퍼니 파트너혜택개발팀에서 즉시할인쿠폰 업무를 담당하고 있는 Kent입니다. 즉시할인쿠폰 파트 내에서 테스트 코드를 작성하는 방법에 대해 소개 해보겠습니다.

테스트 코드는 어플리케이션이 올바르게 동작하고, 반복적인 리팩토링을 통해 깔끔한 코드를 유지하도록 도와줍니다. 하지만 무분별한 테스트 코드 작성은 많은 시간과 노력을 들이게 하고, 유지하는데 많은 비용을 들게 만들기도 합니다. 저희 파트는 ‘ 최대한 쉽고(easy) 가치있는(valuable) 테스트 코드를 작성한다.’ 라는 목표를 가지고 테스트 방법론이나 라이브러리 등 여러가지를 검토해 보고 저희 팀에 알맞은 테스트 작성 방식을 만들어 적용해 보았습니다.

Photo by Ferenc Almasi on Unsplash

가치있는 테스트 작성하기

예를 들어 다음과 같이 테스트를 케이스를 작성합니다.

1. 데이터 베이스에 날짜, 사용자 아이디로 쿠폰이 있는지 확인 후 true, false를 반환한다.
2. 즉시할인 쿠폰은 한 사람당 하루에 한 장 사용이 가능하다.

두 개의 테스트 케이스가 완전히 다른 것 처럼 보이지만, 테스트 코드는 같은 부분을 테스트하고 있습니다. 1번 테스트케이스는 코드수준 레벨에서 테스트 케이스를 작성 한 것이고 2번은 동작 수준에서 테스트를 작성한 것으로 볼 수 있습니다.

코드 레벨 테스트는 목적과 이유가 불분명하며, 코드가 변경되면 테스트 또한 변경될 위험이 높습니다. 그래서 가치가 없는 테스트로 판단합니다.

1번과 같은 테스트 케이스를 만들지 않고 2번과 같이 가치있는 테스트 케이스 작성을 할 수있는 규칙을 만들려 했지만, 쉽지 않았습니다. 그래서 접근 방식을 바꿔 해야할 규칙을 만들기 보단 하면 안되는 규칙을 작성하는 방식으로 접근해 보았고 결과적으로는 많이 개선되었습니다.

규칙 몇 가지를 소개하겠습니다.

  1. 팩토리 메소드는 테스트하지 않는다.

https://gist.github.com/hussard1/5b0d88906578344b9bdf543241a46e5f

비지니스 로직이 포함되지 않은 팩토리 메소드는 테스트를 작성하지 않습니다.

  1. DB 조회/저장/수정 메서드는 테스트하지 않는다.

https://gist.github.com/hussard1/d2bf74e05326c27a214fcbc867aa333f

단순 조회 및 저장 메소드는 테스트하지 않습니다. 필요한 경우에는 DB 저장메소드만 테스트합니다.

  1. Bean Validation 테스트하지 않는다.

https://gist.github.com/hussard1/2cfc0dab620d2d66147bf467d541cae8.js

Bean Validation 역시 비지니스 로직이 포함된 경우만 테스트를 작성합니다.

  1. 테스트 실행 구절은 한 줄 이상이 되지 않는다.
- 제휴점 즉시할인 쿠폰 리스트를 조회하면 같은 가격은 한장만, 가격이 높은 순서대로 노출한다. (x)
- 제휴점 즉시할인 쿠폰 리스트를 조회하면 같은 가격 한장만 노출한다. (o)
- 제휴점 즉시할인 쿠폰 리스트를 조회하면 가격이 높은 순서대로 노출한다. (o)
  1. 테스트 코드는 Application, Domain, Web/Infra, External 등 계층 간 의존성 경계를 넘지 않는다. (계층 간 의존성은 테스트 대역을 사용한다)

테스트 대역(Mock)을 남용하는 것에 대해 좋지 않은 생각을 가지고 있지만, 테스트 대역 사용에 대해 명확한 규칙을 갖기 위해 계층간의 의존성에는 사용하도록 강제하였습니다.

테스트 작성 시, 하면 안되는 규칙을 작성하는 게 가치있는 테스트를 작성하게 하는데 훨씬 도움이 되는 것 같습니다. 팀원과 협의하에 규칙을 만들고 그에 따라 테스트 코드를 작성하시길 추천 드립니다.

쉽게 테스트 작성하기

쉽게 테스트를 작성하기 위해서는 적절한 테스트 도구를 도입하는 것도 중요합니다. 기존에는 Junit5 + AssertJ + Mokito를 사용하였지만, 검토 끝에 Spock 테스트 프레임워크를 도입하였습니다.

우선 아래 코드를 확인해보겠습니다.

https://gist.github.com/hussard1/34873aa5450758c266998a503b0f7bbb

같은 내용을 테스트 하지만 Spock을 사용하여 테스트를 작성하면 코드가 훨씬 간결해 진 것을 확인할 수 있습니다.

그럼 Spock을 사용하면 어떤 장점이 있는지 살펴보겠습니다.

  1. Groovy

Spock은 Groovy로 작성된 테스트코드를 실행하는 프레임워크입니다. Groovy는 동적 타입 언어로, 자바와 호환되고 간결한 구문을 지원합니다. Groovy를 사용하니 테스트 코드가 짧아지고, 가독성이 좋아졌습니다. 또한 Spock은 Groovy가 아닌, Java코드로 작성해도 됩니다.

  1. BDD

given, when, then 등 label 사용을 강제함으로서 BDD코드를 작성하게 도와 줍니다.

https://gist.github.com/hussard1/22483e3a5db4eb2c96279be8bb07d725

  1. 테스트 대역(Stub, Mock, Spy)

테스트 대역을 간단히 작성할 수 있습니다.

https://gist.github.com/hussard1/1767655abf995c323c10b5a7b181d821

  1. 스프링과 호환됩니다.
org.spockframework:spock-spring 

의존성을 추가하면 각종 스프링 테스트 어노테이션 등을 사용할 수 있습니다.

자세한 사용법은 Spock 공식 문서를 참고하시거나 실제 적용 사례는 구글링을 통해 검색하신 문서를 참고하시면 좋습니다. 생각보다 적용하기 매우 쉬우니 사용해 보시길 추천 드립니다.

이 외에도 Spock은 다양한 기능이 있지만, 특히 유용한 몇 가지 기능을 소개해 드릴까 합니다.

Spock의 유용한 기능 소개

  1. where 절

where절은 테스트 구조를 한 번 정의한 다음 다른 입력 값에 대해 동일한 테스트를 실행하는 매개 변수화된 테스트를 지원합니다

https://gist.github.com/hussard1/57cbe9e1348f2fac8df0ef5fe5a80baa

  1. private 변수 접근

setter선언이나 ReflectionTestUtils을 사용하지 않아도 private 변수에 접근 가능합니다.

https://gist.github.com/hussard1/87092dca7b423afa352477c17edc6e12

  1. 테스트 명세 작성

given, when, then 라벨에 명세를 추가하거나 and 구문 을 사용하여 그룹을 분리할 수 잇습니다.

https://gist.github.com/hussard1/ab3aef84b36b856d2e31913599eda007

  1. 테스트 대역 오버라이딩

테스트 대역을 미리 정의했어도 오버라이딩해서 사용이 가능합니다.

https://gist.github.com/hussard1/46e7dbbeeec18fb2eb354306dfbc1d23

이 기능을 활용하여 모든 valid mock을 미리 정의하고 경우에 맞게 오버라이딩하여 사용하면 코드가 훨씬 간단해 집니다.

마치며

테스트를 작성하는 좋은 방법이 많지만, 그 중에서 각자의 팀에 맞게 적절하게 선택하여 적용하는 게 중요하다고 생각합니다. 우선 자신의 개발 팀원들과 협의하에 적절한 규칙과 도구를 정하여 테스트 코드를 작성하고, 시행착오를 겪으면서 점진적으로 개선해 나가시길 바랍니다.

이상 많은 도움이 되셨길 바라며, 마치겠습니다.

끝까지 읽어주셔서 감사합니다.