본문 바로가기

CS 아티클 리뷰

[Martin Fowler] Unit Test

https://martinfowler.com/bliki/UnitTest.html

 

bliki: Unit Test

Unit Tests are focused on small parts of a code-base, defined in regular programming tools, and fast. There is disagreement on whether units should be solitary or sociable.

martinfowler.com

 

1) 개요

- 유닛 테스트는 소프트웨어 개발에서 자주 언급되는 용어이며, 프로그램을 작성하는 동안 항상 익숙했던 용어입니다.

  그러나 대부분의 소프트웨어 개발 용어처럼 명확하게 정의되어 있지 않아서 사람들이

  그것을 실제보다 더 엄밀하게 정의된 것으로 생각할 때 혼란이 발생할 수 있습니다.

 

- 제가 처음 유닛 테스트에 본격적으로 노출된 것은 Kent Beck과 함께 일하면서

  Xunit 유닛 테스트 도구를 사용하기 시작했을 때였습니다.

  (사실 저는 이 스타일의 테스트를 "xunit 테스트"라고 부르는 것이 좋다고 생각하기도 합니다.)

  유닛 테스트는 또한 익스트림 프로그래밍(XP)의 대표적인 활동이 되었으며, 테스트 주도 개발로 빠르게 이어졌습니다.

   

- XP에서 유닛 테스트를 사용하는 것에 대한 정의상의 우려는 초기부터 존재했습니다.

  저는 Usenet 토론 그룹에서 XP 개발자들이 한 테스트 전문가에게 "유닛 테스트"라는 용어를

  잘못 사용하고 있다고 비판받던 기억이 생생합니다.

  우리는 그에게 정의를 물었고, 그는 "내 교육 과정의 첫날 오전에 유닛 테스트의 24가지 다른 정의를 다룹니다."

  라는 식으로 답변했습니다.

 

- 다양한 정의가 존재하지만, 공통적인 요소도 있습니다.

  첫째, 유닛 테스트는 소프트웨어 시스템의 작은 부분에 집중하는 저수준 테스트라는 개념이 있습니다.

  둘째, 요즘 유닛 테스트는 일반적으로 프로그래머들이 자신들의 정규 도구를 사용하여 작성하며,

  유일한 차이점은 유닛 테스트 프레임워크를 사용하는 것입니다.

  셋째, 유닛 테스트는 다른 종류의 테스트보다 상당히 빨라야 합니다.

 

- 그래서 공통적인 요소가 있지만, 차이점도 존재합니다. 한 가지 차이점은 사람들이 유닛을 어떻게 정의하느냐입니다.

  객체 지향 설계에서는 클래스가 유닛으로 간주되는 반면,

  절차적 또는 함수적 접근법에서는 단일 함수가 유닛으로 간주될 수 있습니다.

  그러나 실제로는 상황에 따라 달라지며, 팀이 시스템과 테스트를 이해하는 데 적합한 방식으로 유닛을 결정합니다.

  저는 유닛을 클래스로 시작하지만, 종종 밀접하게 관련된 여러 클래스를 하나의 유닛으로 취급하기도 합니다.

  드물게는 클래스의 일부 메서드를 유닛으로 간주하기도 합니다. 어떻게 정의하든 상관없습니다.

 

 

2) 고립된 테스트 or 사교적 테스트? 

 

- 고립된 테스트와 사교적인 테스트 더 중요한 구분은 테스트하는 유닛이

  고립된(Solitary) 것인지 사교적인(Sociable) 것인지입니다.

 

- 주문 클래스의 price 메서드를 테스트한다고 가정해 보세요.

  price 메서드는 제품 및 고객 클래스의 함수를 호출해야 합니다.

  고립된 유닛 테스트를 선호하는 경우, 실제 제품 또는 고객 클래스를 여기서 사용하지 않으려 할 것입니다.

  왜냐하면 고객 클래스에 결함이 있으면 주문 클래스의 테스트가 실패할 것이기 때문입니다.

  대신 협력자를 위한 테스트 더블(Test Doubles)을 사용합니다.

 

- 하지만 모든 유닛 테스터가 고립된 유닛 테스트를 사용하는 것은 아닙니다.

  실제로 xunit 테스트가 90년대에 시작되었을 때,

  우리는 협력자와의 통신이 어려운 경우(예: 원격 신용 카드 인증 시스템) 외에는 고립된 방식을 시도하지 않았습니다.

  우리는 실제 결함을 찾아내는 것이 어렵지 않았기 때문에 사교적인 테스트를 허용하는 것이

  실제로 문제가 되지 않는다고 생각했습니다.

 

- 사교적인 유닛 테스트를 사용하는 것은 우리가 "유닛 테스트" 용어를 사용한 것에 대해

  비판받은 이유 중 하나였습니다.     

  저는 이 용어가 적절하다고 생각하는데, 이는 이러한 테스트가 단일 유닛의 동작을 테스트하기 때문입니다.

  우리는 그 유닛 외의 모든 것이 제대로 작동한다고 가정하고 테스트를 작성합니다.

 

- xunit 테스트가 2000년대에 인기를 끌면서 고립된 테스트의 개념이 다시 등장했으며,

  적어도 일부 사람들에게는 더욱 강조되었습니다.

  모의 객체(Mock Objects)와 모킹을 지원하는 프레임워크가 등장했습니다.

  두 가지 xunit 테스트 스타일, 즉 고전적인(Classic) 스타일과 모키스트(Mockist) 스타일이 발전했습니다.

 

- 두 스타일의 차이점 중 하나는 모키스트가 고립된 유닛 테스트를 고집하는 반면,

  고전적인 스타일은 사교적인 테스트를 선호한다는 것입니다.

  오늘날 저는 두 가지 스타일의 xunit 테스터를 모두 알고 존경합니다

  (개인적으로는 고전적인 스타일을 고수하고 있습니다).

 

- 고전적인 테스터인 저도 협력이 어려운 경우 테스트 더블을 사용합니다.

  원격 서비스와의 통신에서 비결정론을 제거하는 데 매우 유용합니다.

  실제로 일부 고전적인 xunit 테스터는 외부 자원(예: 데이터베이스 또는 파일 시스템)과의 모든 협력에 대해

  더블을 사용해야 한다고 주장합니다.

  이는 비결정론 위험 때문이기도 하고, 속도 때문이기도 합니다.

 

- 저는 이것이 유용한 지침이라고 생각하지만, 외부 자원에 대한 더블 사용을 절대적인 규칙으로 여기지 않습니다.

  자원과의 통신이 안정적이고 충분히 빠르다면 유닛 테스트에서 이를 사용하는 데 아무런 문제가 없습니다.

  속도 유닛 테스트의 공통된 특성 — 작은 범위, 프로그래머가 직접 수행, 빠른 속도 — 덕분에 프로그래밍할 때

  매우 자주 실행할 수 있습니다. 실제로 이것이 SelfTestingCode의 주요 특징 중 하나입니다.

 

- 이 상황에서 프로그래머는 코드에 변경을 가한 후에 유닛 테스트를 실행합니다.

  저는 컴파일할 만한 가치가 있는 코드가 있을 때마다 유닛 테스트를 여러 번 실행합니다.

  그렇게 하는 이유는 무언가를 실수로 망가뜨렸을 때 즉시 알고 싶기 때문입니다.

  마지막 변경으로 결함을 도입했을 경우, 찾을 범위가 적어서 버그를 발견하기가 훨씬 쉽습니다.

 

- 유닛 테스트를 그렇게 자주 실행할 때는 모든 유닛 테스트를 실행하지 않을 수도 있습니다.

  보통 현재 작업 중인 코드 부분에서 작동하는 테스트만 실행하면 됩니다.

  일반적으로는 테스트 스위트를 실행하는 데 걸리는 시간과 테스트의 깊이 사이에 절충이 필요합니다.

  저는 이를 컴파일할 때마다 실행하는 스위트이기 때문에 컴파일 스위트라고 부릅니다.

  이는 인터프리터 언어인 Ruby에서도 마찬가지입니다.

 

- 지속적 통합(Continuous Integration)을 사용하는 경우, 이를 일부로 테스트 스위트를 실행해야 합니다.

  이 스위트는 모든 유닛 테스트를 포함하는 것이 일반적입니다.

  또한 몇 가지 BroadStackTests를 포함할 수도 있습니다.

  프로그래머는 하루에 여러 번 이 커밋 스위트를 실행해야 하며,

  버전 제어에 대한 공유 커밋 전에 반드시 실행해야 하고, 휴식 시간이나 회의 참석 등의 기회가 있을 때마다

  실행해야 합니다. 커밋 스위트가 빠를수록 더 자주 실행할 수 있습니다.

 

- 사람마다 유닛 테스트와 테스트 스위트의 속도에 대한 기준이 다릅니다.

  David Heinemeier Hansson은 몇 초가 걸리는 컴파일 스위트와 몇 분이 걸리는 커밋 스위트에 만족합니다.

  Gary Bernhardt는 그것을 참을 수 없을 정도로 느리다고 생각하며, 300ms 정도의 컴파일 스위트를 고집합니다.

  Dan Bodart는 커밋 스위트가 10초를 넘지 않기를 원합니다.

 

- 여기에 대한 절대적인 답은 없다고 생각합니다.

   개인적으로 저는 1초 이하의 컴파일 스위트와 몇 초 걸리는 스위트의 차이를 느끼지 못합니다.

   Kent Beck의 경험칙에 따르면 커밋 스위트는 10분을 넘지 않아야 합니다.

   하지만 진정한 요점은 테스트 스위트가 충분히 빨리 실행되어 자주 실행하는 데 방해가 되지 않아야 한다는 것입니다.

   그리고 자주 실행한다는 것은 버그를 발견했을 때 찾아볼 작업량이 충분히 적어서

   빠르게 찾을 수 있을 정도로 자주 실행하는 것을 의미합니다.