달력

05

« 2012/05 »

  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  

'테스트기법/xUnit테스트패턴'에 해당되는 글 3

  1. 2010/06/05 테스트 자동화의 원칙 (2)
  2. 2010/04/21 테스트 자동화의 철학
  3. 2010/04/14 테스트 자동화의 목표
세계 최초의 법전을 알고 계시나요? 바로 함무라비 법전입니다. (수메르 법전이라는 설도 있으나 여기서는 언급하지 않겠습니다.) 고대 바빌로니아의 함무라비 왕이 제정한 법전입니다. 왜 이러한 법전을 만들게 되었을까요? 통치의 일관성이나 사회(또는 국가) 전체가 일정한 관계를 유지하기 위해서는 질서가 필요하게 되었고, 그것을 위해서 만들어진 질서가 바로 법입니다.



갑자기 법전 이야기를 왜 하냐구요? 지난 포스트에서 테스트 자동화의 철학에 따라서 어떻게 자동화할 것인지에 많은 영향을 준다는 점을 알아보았습니다.(참고 : [Test/xUnit Test Patterns] - 테스트 자동화의 철학) 이러한 상황에서 테스트 작성시 일정한 질서를 유지하기 위해서는 반드시 테스트 자동화를 위한 원칙이 존재해야 한다는 것이지요. 그래야 테스트를 읽는 사람도 유지보수를 하는 사람도 쉽게 테스트 코드에 적응을 할 수 있을테니까요.

그럼 테스트 자동화의 원칙에 대해서 하나하나 알아가보겠습니다.

1. 테스트를 먼저 작성하라.

테스트를 먼저 작성하면 디버깅을 줄여주어서 테스트 자동화에 드는 노력을 충분히 보상해줄 뿐만 아니라 테스트하기 쉬운 설계가 자연스럽게 나오게 됩니다.


2. 테스트하기 쉽게 설계하라.

테스트하기 쉽게 설계하지 않으면 나중에 테스트를 도입하는 것은 굉장한 노력이 듭니다. 레거시 코드에서 자동화 테스트를 도입하고자 하면 처음부터 테스트하기 쉽게 설계했을 때보다 훨씬 많은 노력이 듭니다.


3. 정문을 먼저 사용하라.

되도록 public으로 공개된 인터페이스를 통해서 테스트를 진행하세요. 테스트 유틸리티 등으로 리플렉션을 이용하여 데이터를 조작하거나 모의객체를 사용하여 동작 검증을 많이 한다면 결합도가 높고 깨지기 쉬운 테스트가 됩니다. 

public 인터페이스로 객체를 테스트하고 상태 검증으로 제대로 동작하는지 확인할 것을 권장합니다.


4. 의도를 드러내라.

해당 테스트가 어떠한 것을 검증하고자 하는지 의도를 들어내는 것이 중요합니다. 즉, 테스트를 읽었을 때 이것이 어떤 테스트인지 한눈에 알아볼 수 있도록 해야합니다. 그렇게 하려면 테스트코드가 길지 않아야 하며, 테스트 내에 조건문이 있어도 안됩니다. 

이를 위해서 의도가 드러나는 테스트 이름의 테스트 유틸리티 메소드를 적극적으로 사용하시기 바랍니다. 또한 테스트 메소드에서 어떤 값이 입력되서 어떤 값이 출력되는지 잘 알아볼 수 있도록 테스트 코드를 리팩토링하시는 것도 중요합니다.


5. SUT를 고치지 마라.

테스트를 효과적으로 만들기 위해서 모의 객체나 테스트를 위한 하위 클래스를 만들수 있습니다. 이 때 주의할 점은 테스트 대상이 아닌 다른 것들(다른 인터페이스를 호출하는 것)은 대체해도 되지만 테스트 대상 자체(SUT)를 변경하지는 말아야 합니다. 이를 변경하는 것은 실제 제품코드가 아닌 테스트용 제품코드를 만들어 테스트하는 것이기 때문입니다.


6. 테스트를 독립적으로 유지하라.

테스트는 혼자서 실행될 수 있어야 합니다. 통합테스트 또는 인수테스트라면 모를까 단위테스트에 있어서는 반드시 독립적으로 동작을 할 수 있어야 합니다. 단위테스트는 빠른 피드백과 작은 유지비용을 유지하여 최대한 효과를 거두는 것이 중요한대 테스트가 독립적으로 동작하지 않으면 유지비용이 커져 테스트의 효과가 반감하게 됩니다.

이를 위해서 신선한 픽스처를 설치하는 것이 중요합니다.


7. SUT를 격리하라.

테스트는 외부 환경에 영향을 받아서는 안됩니다. 외부환경에 영향을 받게 되면 테스트하기 힘들뿐만 아니라 테스트 결과의 일관성을 해치기 때문에 테스트 결과를 신뢰할 수 없게 됩니다.

예를 들어, 특정 운영체제에 종속적이거나 시간에 변화에 영향을 받는 시스템이라면 테스트는 갑자기 깨질수 있습니다. 그렇기때문에 외부 환경과 SUT(테스트 대상)을 분리하는 것이 좋습니다.

의존 주입(DI)나 모의 객체 등을 잘 이용하면 SUT를 분리하고 테스트를 좀 더 반복 가능하고 견고하게 만들 수 있습니다.


8. 겹치는 테스트를 최소화하라.

만약 테스트를 할 때 모든 조합 또는 시나리오에 대해서 테스트하기란 불가능합니다. 따라서 작성해야할 테스트를 잘 선택하는 것이 중요합니다. 필요할 경우 적절한 테스팅 기법을 동원하는 것이 좋습니다. 특정 기능에 의존하는 테스트의 수를 최대한 작게 만들어야 합니다.


9. 테스트할 수 없는 코드를 최소화하라.

공중그네타기 연습을 하는데 안전망에 구멍이 많이 뚫려있다면 연습을 하기 꺼려질 것입니다. 마찬가지로 테스트 할 수 없는 코드가 많을수록 리팩토링을 하기도 힘들어지고 버그가 발생할 확률이 높아집니다.

되도록 환경과 테스트 대상을 잘 분리하고 변하는 부분과 변하지 않는 부분을 잘 가려내서 가능한한 많은 코드가 테스트 될 수 있도록 하세요.


10. 테스트 로직을 제품 코드에 넣지 마라.

테스트는 시스템의 동작을 검증하는 행위이기 때문에 테스트 대상이 되는 시스템에 테스트 코드를 넣지 마세요. 


11. 테스트별로 하나의 조건만 검증하라.

테스트 메소드 하나에 여러가지 경우의 수를 테스트하면 테스트 의도가 드러나지 않습니다. 또한 유지보수하기도 어려워지며, 테스트를 읽는 것이 매우 힘들어지죠. 따라서 각 스크립트 기반 테스트는 반드시 한 가지 테스트 조건만 검증하길 바랍니다. 결함 국소화를 위해서라도 테스트는 한가지 조건만 검증하는 것이 좋습니다.


12. 따로 테스트 하라.

복잡한 애플리케이션은 수많은 작은 동작들을 결합하여 하나의 동작을 하기도 합니다. 이를 한꺼번에 검증하지 말고 각각의 작은 동작들을 따로따로 테스트하는 것이 중요합니다. 11번째 원칙과 마찬가지로 하나의 테스트가 너무 많은 것을 테스트하려고 해서는 안됩니다. (이는 결국 하나의 클래스가 너무 많은 책임을 지니는 것과 동일합니다.)


13. 효과와 책임을 적당하게 맞추어라.

테스트를 작성하고 수정하는 데 드는 노력이 해당 기능을 구현하는 데 드는 노력보다 더 들어서는 안됩니다. 일반적인 단위테스트이거나 DBUnit을 이용한 DAO 테스트라면 TDD를 적극적으로 이용하세요. 만약 모의객체를 이용하는 테스트라면 TDD를 사실상 하기는 힘듭니다. 이때는 구현을 먼저하세요.

테스트를 작성해서 얻는 이득이 적다면(예를 들어, 상위 레이어의 메소드를 그대로 호출하는 인터페이스와 같은 경우) 굳이 테스트를 만들 필요가 없습니다.


지금까지 테스트 자동화의 원칙에 대해서 알아보았습니다. 모든 원칙이나 패턴이 그렇듯이 무작정 지키려는 것은 의미가 없습니다. 원칙에서 요구하는 의도를 정확히 파악하고, 그 의도에 맞다고 판단이 되면 따르세요. 

빠르게 이동해야하는 고속도로에서 무작정 준법정신을 지켜 최소속도만 유지하고 간다면 교통은 마비될 것입니다. 적당히 속도를 내는 것이 현실적으로 중요합니다.(물론 법규를 지키는 한도내에서 해야겠죠 ^^)

자신만의 테스트 자동화 원칙을 세우고 그 룰에 따르시기 바랍니다.




저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

'테스트기법 > xUnit테스트패턴' 카테고리의 다른 글

테스트 자동화의 원칙  (2) 2010/06/05
테스트 자동화의 철학  (0) 2010/04/21
테스트 자동화의 목표  (0) 2010/04/14
Posted by 스쿨쥐
여러분들은 어떤 폰을 쓰고 계신가요? 제가 다니는 회사에서는 굉장히 많은 분들이 아이폰으로 바꾸셨습니다. 옆에서 보고 있으면 너무너무 탐이 날 정도로 좋은 폰이더군요. 애플은 아이폰도 모자라서 아이패드를 출시함으로써 최근에 굉장히 잘나가고 있지요.

왜 우리나라 기업들은 그런 제품을 만들지 못했을까요? 모IT 기업의 대표의 말을 빌리자면 "스티브잡스는 우리에게 문화를 선물했고, 삼성은 우리에게 광고를 선물했다. 즉, 애플의 기술이 아니라 애플의 철학에 열광한 것이다."라고 하더군요. 이처럼 경영철학은 기업의 성공에 있어서 굉장한 영향력을 가집니다.


테스트 자동화에도 철학이 중요합니다. 테스트에 관한 철학은 테스트를 어떻게 자동화할지에 대해서 많은 영향을 주기 때문입니다. xUnit 계통 테스트 자동화를 다르게 쓰는 이유가 바로 이러한 철학적인 차이 때문입니다. 예를 들어 모의 객체(Mock Object)를 잘 쓰지 않는 사람이 있는 반면 모의 객체를 자주 쓰는 사람들도 있습니다. 

그렇다면 테스트 자동화에 관련되어 어떠한 철학적 차이들이 있을까요?
  1. 테스트가 먼저냐 테스트가 나중이냐?
  2. 테스트냐 예제냐?
  3. 단계별 테스트냐 한꺼번에 테스트냐?
  4. 밖에서 안으로냐 안에서 밖으로냐?
  5. 상태검증이냐 동작검증이냐?
  6. 픽스처를 먼저 설계하냐 테스트별로 픽스처를 설계하냐?

이제부터 하나하나 비교해보겠습니다.

1. 테스트가 먼저냐 테스트가 나중이냐?

사실 이 차이는 논란이 줄어듭니다. 테스트의 경험을 쌓으면 쌓을수록 TDD 즉, 테스트를 먼저하게 됩니다. 테스트를 먼저하게 되면 테스트를 만들기 쉽고 제품코드가 간결해지며 꼭 필요한 메서드만 작성하게 됩니다. 레거시 코드에서 테스트를 작성해본 경험이 있다면 테스트를 나중에 도입하는 것이 얼마나 힘든일인지 잘 알게 됩니다.

한가지 예외가 있다면 모의 객체(Mock Object)를 사용하게 되면 테스트와 제품코드가 번갈아가며 작성되는 것을 알 수 있습니다. 하지만 이 경우에도 최초에는 테스트를 먼저 작성하게 되며, 제품코드가 추가됨에 따라 테스트에 모의 객체가 추가되는 것을 알 수 있습니다.


2. 테스트냐 예제냐?

테스트가 테스트의 의미로 작성할지 아니면 예제의 의미로서 작성할 지 분분합니다. 우리가 새로운 API나 기술셋을 익힐 때를 생각해봅시다. 아마 예제코드를 보고 따라 작성하면서 사용법을 익힐 것입니다. 이처럼 우리가 테스트를 예제로 작성한다면 테스트가 바로 "실행가능한 명세"가 되어 테스트자동화의 목표를 이루게 됩니다.

개인적인 의견으로는 처음에는 기능을 구현하여 "시스템이 돌아가는 것을 증명"하고, 버그가 발견되면 테스트를 통해 재현을 하여 "시스템이 돌아가지 않음"을 증명하면 됩니다. 결국 내가 만든 시스템이 어떠한 상황에서 돌아가고 어떻게 사용하며, 어떠한 상황에서 돌아가지 않는지에 대한 상세한 설명이 됩니다.


3. 단계별 테스트냐 한꺼번에 테스트냐?

단위테스트의 경우에는 세분화된 테스트와 점진적인 개발을 하게되면 테스트가 왜 실패했는지가 분명하기 때문에 디비거를 사용할 일이 많이 줄어듭니다.
고객테스트의 경우에는 사용자 스토리에 따른 테스트를 하나씩 제공하기가 힘듭니다. 이런 경우에는 차라리 테스트를 통하여 사용자 스토리를 구성해나가는 것이 좋습니다.


4. 밖에서 안으로냐 안에서 밖으로냐?

고객처럼 생각하기 위해서는 밖에서 안으로 설계를 해 나가는 것이 좋습니다. 하지만 테스트를 만들때마다 테스트 스텁이나 모의 객체를 써야한다는 불편한 점이 있습니다. 그래서 개인적으로는 설계는 밖에서 안으로 들어오고, 실제 구현은 안에서 밖으로 해 나가는 것이 괜찮은 방법 중 하나라고 생각합니다.


5. 상태 검증이냐 동작 검증이냐?

동작 검증은 상태 검증보다는 좀 더 세밀한 단계입니다. 동작 검증이 좋기는 하나 몇가지 단점이 있습니다. 첫째로 테스트 유지 보수 비용이 높으며, 둘째로 참조하는 인터페이스가 다른 것으로 변경되면 테스트도 쉽게 깨져버립니다. 즉, 테스트가 구현에 의존하는 경향이 나타납니다. 사실 테스트 만들기도 굉장히 귀찮지요 ^^;

그래서 개인적인 의견으로는 상태 검증을 주로 하되 동작 검증이 필요한 경우에만 추가적으로 만드는 것이 좋을 것 같습니다.


6. 픽스처를 먼저 설계하냐 테스트별로 픽스처를 설계하냐?

사실 TDD를 하다 보면 테스트 메소드별로 최소 픽스처를 사용하게 됩니다. 항상 신선한 픽스처를 사용하는 것이지요. 다만 픽스처를 먼저 설계해야 할 경우가 간혹 있습니다. 예를들어, DBUnit으로 dao를 테스트를 한다거나, 사용자 시나리오 테스트(또는 통합 테스트)를 할 경우가 있겠지요. 

꼭 필요한 부분이 아니라면 테스트 메소드별로 신선한 픽스처를 쓰는 것이 좋습니다.


지금까지 테스트 자동화에 대한 철학을 알아보았습니다. 어느 것이 맞다 틀리다의 문제가 아닙니다. 철학은 다르지만 목표는 같습니다. 높은 품질의 소프트웨어를 목적에 맞게, 제때에 개발해내는 것이지요. 다만 그 목표를 어떻게 달성할 것인지만 다를 뿐입니다. 서로 다른 철학을 이해하고 서로의 공통점을 찾아서 나아가는 것이 중요합니다.


여러분은 어떤 철학을 가지고 계시나요?

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

'테스트기법 > xUnit테스트패턴' 카테고리의 다른 글

테스트 자동화의 원칙  (2) 2010/06/05
테스트 자동화의 철학  (0) 2010/04/21
테스트 자동화의 목표  (0) 2010/04/14
Posted by 스쿨쥐
저는 어릴때부터 축구를 좋아해서 지금까지도 축구를 즐겨하고 있습니다. 흔히 말하는 "조기축구"를 하고 있지요. 다들 아시다싶이 축구는 골(goal)을 넣어야 이기는 게임입니다. 궁극적인 목적은 골을 많이 넣어서 이기는 것입니다. 


테스트도 이와 마찬가지입니다. 테스트를 왜 해야하는가는 이전글([Test] - 테스트, 정말 필요한가요?)을 참조하세요. 이번에는 테스트 자동화가 달성해야할 목표(goal)에 대해서 이야기하려 합니다.



먼저 테스트를 통해서 얻을 수 있는 가치에 대한 목표에 대해서 알아봅시다.

  1. 테스트는 품질 향상에 도움이 되어야 합니다.
  2. 테스트는 테스트 대상 시스템(System Under Test, SUT)을 이해하는 데 도움이 되어야 합니다.
  3. 테스트는 위험을 줄여야 합니다.

테스트가 품질향상에 도움이 되기 위해서는 명세로서의 테스트를 달성해야 합니다. 즉, 사용자가 SUT를 사용하는 방식을 실제 그대로 반영해야 하여야 합니다. 다른 이름으로 실행 가능한 명세라고도 합니다. 또한 회귀 테스트를 통해 버그 발생을 막아주어야 하며, 문제 범위를 좁혀서 왜 잘 돌아가지 않는지 알려주어야 합니다. 이를 결함 국소화라고 합니다.

SUT를 이해하는 데 도움이 되기 위해서는 특정 값이 입력되었을 때 어떠한 결과가 나올지를 테스트를 통해서 알려주어야 합니다. 테스트를 읽는 사람이 테스트를 문서로서 활용할 수 있도록 해야합니다. 블랙박스 컴포넌트 테스트의 경우 사실상 소프트웨어 컴포넌트의 요구 사항을 보여줍니다.

테스트를 통해서 위험을 줄일 수 있어야 합니다. 서커스에서 그물 없이 공중그네 타기를 배운다고 한다면 기술을 시도하기 무척이나 꺼려질 것입니다. 자칫 목숨이 위험할테니까요. 테스트는 이런 상황에서 안전망이 됩니다. 레거시 코드를 변경할 때 회귀 테스트를 통해서 버그가 발생할 위험을 줄여줍니다. 

반대로 그물이 공중그네 타기에 방해가 되어서는 안되겠지요. 마찬가지로 테스트가 제품코드에 해가 되지 않아야 합니다.



다음은 테스트 자체가 가져야 할 목표에 대해서 알아봅시다.

  1. 테스트는 실행하기 쉬워야 합니다.
  2. 테스트는 만들고 유지하기가 쉬워야 합니다.
  3. 테스트는 시스템이 발전해 나감에 따라 필요한 유지 보수 비용이 최소화 되어야 합니다.

테스트가 실행하기 어렵다면 아무도 테스트를 실행하지 않을 것입니다. 그래서 테스트를 쉽게 실행하기 위해서는 완전 자동으로 돌아가야 할 것이고, 직접 검사하지 않더라도 알아서 에러를 찾아내 알려줄 수 있는 자체 검사 테스트가 되어야 합니다. 그리고 몇번이고 돌려도 같은 결과가 나오는 반복되는 테스트여야 하며, 테스트별로 따로 돌려볼 수 있는 독립적인 테스트가 되어야 합니다.

테스트를 만들고 유지하는 데 많은 노력이 든다면 테스트를 작성하지 않겠지요. 그래서 테스트를 단순하게 만들어서 쉽게 만들 수 있게 하고, 테스트 유틸리티 메서드도 제공하면 더욱 좋습니다. 또한 하나의 테스트는 하나의 관심만 테스트를 하는 것이 테스트하기에도 쉬운 설계의 핵심이 됩니다.

위의 목표를 잘 달성을 했다면 시스템이 발전하는 동안에 제품코드도 변하고, 테스트 코드도 변하는 상황에서 영향을 받는 테스트의 수가 최소가 될 것입니다. 즉, 견고한 테스트가 되는 것이지요. 이렇게 되면 테스트를 유지하는 비용도 최소화가 됩니다.



인생에서 목표가 없으면 길을 잃고 해맬수 있습니다. 테스트 또한 분명한 목표를 가지고 테스트를 해야만 방향을 잃지 않고 테스트로 인한 혜택을 누릴 수 있지 않을까요?
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

'테스트기법 > xUnit테스트패턴' 카테고리의 다른 글

테스트 자동화의 원칙  (2) 2010/06/05
테스트 자동화의 철학  (0) 2010/04/21
테스트 자동화의 목표  (0) 2010/04/14
Posted by 스쿨쥐